From a69abdd90db3fc471c975d7cd9146d3c5ec6d30b Mon Sep 17 00:00:00 2001 From: Austin Vazquez Date: Mon, 20 Oct 2025 15:11:53 -0500 Subject: [PATCH] api/types/system: add type specific usage fields to `DiskUsage` This change adds type specific fields to `GET /system/df` endpoint with high level information of disk usage. This change also introduces `verbose` query to the endpoint so that detailed information is by default excluded unless queried to reduce memory consumption. The previous top level `DiskUsage` fields (`Images`, `Containers`, `Volumes` and `BuildCache`) are now deprecated and kept for backwards compatibility. Co-authored-by: Claude Signed-off-by: Austin Vazquez --- api/docs/CHANGELOG.md | 7 + api/docs/v1.52.yaml | 282 +++++++++++------- api/swagger.yaml | 278 ++++++++++------- api/types/system/build_cache_disk_usage.go | 40 +++ api/types/system/containers_disk_usage.go | 40 +++ api/types/system/disk_usage.go | 28 +- api/types/system/images_disk_usage.go | 40 +++ api/types/system/volumes_disk_usage.go | 40 +++ client/client_interfaces.go | 4 +- client/client_mock_test.go | 11 + client/system_disk_usage.go | 240 ++++++++++++++- client/system_disk_usage_opts.go | 10 - client/system_disk_usage_test.go | 128 +++++++- daemon/daemon.go | 3 +- daemon/disk_usage.go | 111 ++++--- daemon/server/backend/disk_usage.go | 11 + daemon/server/router/system/system_routes.go | 117 ++++++-- hack/generate-swagger-api.sh | 7 + integration/system/disk_usage_test.go | 231 ++++++++------ .../types/system/build_cache_disk_usage.go | 40 +++ .../api/types/system/containers_disk_usage.go | 40 +++ .../moby/moby/api/types/system/disk_usage.go | 28 +- .../api/types/system/images_disk_usage.go | 40 +++ .../api/types/system/volumes_disk_usage.go | 40 +++ .../moby/moby/client/client_interfaces.go | 4 +- .../moby/moby/client/system_disk_usage.go | 240 ++++++++++++++- .../moby/client/system_disk_usage_opts.go | 10 - 27 files changed, 1633 insertions(+), 437 deletions(-) create mode 100644 api/types/system/build_cache_disk_usage.go create mode 100644 api/types/system/containers_disk_usage.go create mode 100644 api/types/system/images_disk_usage.go create mode 100644 api/types/system/volumes_disk_usage.go delete mode 100644 client/system_disk_usage_opts.go create mode 100644 vendor/github.com/moby/moby/api/types/system/build_cache_disk_usage.go create mode 100644 vendor/github.com/moby/moby/api/types/system/containers_disk_usage.go create mode 100644 vendor/github.com/moby/moby/api/types/system/images_disk_usage.go create mode 100644 vendor/github.com/moby/moby/api/types/system/volumes_disk_usage.go delete mode 100644 vendor/github.com/moby/moby/client/system_disk_usage_opts.go diff --git a/api/docs/CHANGELOG.md b/api/docs/CHANGELOG.md index cf5c92c2ab..12273c7aad 100644 --- a/api/docs/CHANGELOG.md +++ b/api/docs/CHANGELOG.md @@ -71,6 +71,13 @@ keywords: "API, Docker, rcli, REST, documentation" part of the `Resource` requirements. * `GET /containers/{id}/stats` now returns an `os_type` field to allow platform- specific handling of the stats. +* `GET /system/df` returns `ImagesUsage`, `ContainersUsage`, `VolumesUsage`, and + `BuildCacheUsage` fields with brief system disk usage data for each system object type. + The endpoint supports the `?verbose=1` query to return verbose system disk usage information. +* Deprecated: `GET /system/df` response fields `LayersSize`, `Images`, `Containers`, + `Volumes`, and `BuildCache` are deprecated in favor of the type specific usage fields. + The legacy fields will not be populated for new API versions that specify the `verbose` + query. ## v1.51 API changes diff --git a/api/docs/v1.52.yaml b/api/docs/v1.52.yaml index 7c0d9a0b7d..52e0e5c18b 100644 --- a/api/docs/v1.52.yaml +++ b/api/docs/v1.52.yaml @@ -2059,6 +2059,47 @@ definitions: x-nullable: true $ref: "#/definitions/OCIDescriptor" + ImagesDiskUsage: + type: "object" + description: | + ImagesDiskUsage represents system data usage for image resources. + properties: + ActiveImages: + description: | + Count of active images. + type: "integer" + format: "int64" + example: 1 + TotalImages: + description: | + Count of all images. + type: "integer" + format: "int64" + example: 4 + Reclaimable: + description: | + Disk space that can be reclaimed by removing unused images. + type: "integer" + format: "int64" + example: 12345678 + TotalSize: + description: | + Disk space in use by images. + type: "integer" + format: "int64" + example: 98765432 + Items: + description: | + List of image summaries. + type: "array" + x-omitempty: true + items: + x-nullable: true + x-go-type: + type: Summary + import: + package: github.com/moby/moby/api/types/image + AuthConfig: type: "object" properties: @@ -2201,6 +2242,47 @@ definitions: is set to `-1` if the reference-count is not available. x-nullable: false + VolumesDiskUsage: + type: "object" + description: | + VolumesDiskUsage represents system data usage for volume resources. + properties: + ActiveVolumes: + description: | + Count of active volumes. + type: "integer" + format: "int64" + example: 1 + TotalVolumes: + description: | + Count of all volumes. + type: "integer" + format: "int64" + example: 4 + Reclaimable: + description: | + Disk space that can be reclaimed by removing inactive volumes. + type: "integer" + format: "int64" + example: 12345678 + TotalSize: + description: | + Disk space in use by volumes. + type: "integer" + format: "int64" + example: 98765432 + Items: + description: | + List of volumes. + type: "array" + x-omitempty: true + items: + x-nullable: true + x-go-type: + type: Volume + import: + package: github.com/moby/moby/api/types/volume + VolumeCreateRequest: description: "Volume configuration" type: "object" @@ -2628,6 +2710,8 @@ definitions: type: "string" x-omitempty: false example: "02:42:ac:13:00:02" + x-go-type: + type: HardwareAddr IPv4Address: type: "string" x-omitempty: false @@ -2772,6 +2856,47 @@ definitions: type: "integer" example: 26 + BuildCacheDiskUsage: + type: "object" + description: | + BuildCacheDiskUsage represents system data usage for build cache resources. + properties: + ActiveBuildCacheRecords: + description: | + Count of active build cache records. + type: "integer" + format: "int64" + example: 1 + TotalBuildCacheRecords: + description: | + Count of all build cache records. + type: "integer" + format: "int64" + example: 4 + Reclaimable: + description: | + Disk space that can be reclaimed by removing inactive build cache records. + type: "integer" + format: "int64" + example: 12345678 + TotalSize: + description: | + Disk space in use by build cache records. + type: "integer" + format: "int64" + example: 98765432 + Items: + description: | + List of build cache records. + type: "array" + x-omitempty: true + items: + x-nullable: true + x-go-type: + type: CacheRecord + import: + package: github.com/moby/moby/api/types/build + ImageID: type: "object" description: "Image ID or Digest" @@ -2914,6 +3039,8 @@ definitions: MAC address for the endpoint on this network. The network driver might ignore this parameter. type: "string" example: "02:42:ac:11:00:04" + x-go-type: + type: HardwareAddr Aliases: type: "array" items: @@ -5453,6 +5580,47 @@ definitions: type: "integer" example: 0 + ContainersDiskUsage: + type: "object" + description: | + ContainersDiskUsage provides system data usage information for container resources. + properties: + ActiveContainers: + description: | + Count of active containers. + type: "integer" + format: "int64" + example: 1 + TotalContainers: + description: | + Count of all containers. + type: "integer" + format: "int64" + example: 4 + Reclaimable: + description: | + Disk space that can be reclaimed by removing inactive containers. + type: "integer" + format: "int64" + example: 12345678 + TotalSize: + description: | + Disk space in use by containers. + type: "integer" + format: "int64" + example: 98765432 + Items: + description: | + List of container summaries. + type: "array" + x-omitempty: true + items: + x-nullable: true + x-go-type: + type: Summary + import: + package: github.com/moby/moby/api/types/container + Driver: description: "Driver represents a driver (network, logging, secrets)." type: "object" @@ -10332,106 +10500,14 @@ paths: type: "object" title: "SystemDataUsageResponse" properties: - LayersSize: - type: "integer" - format: "int64" - Images: - type: "array" - items: - $ref: "#/definitions/ImageSummary" - Containers: - type: "array" - items: - $ref: "#/definitions/ContainerSummary" - Volumes: - type: "array" - items: - $ref: "#/definitions/Volume" - BuildCache: - type: "array" - items: - $ref: "#/definitions/BuildCache" - example: - LayersSize: 1092588 - Images: - - - Id: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" - ParentId: "" - RepoTags: - - "busybox:latest" - RepoDigests: - - "busybox@sha256:a59906e33509d14c036c8678d687bd4eec81ed7c4b8ce907b888c607f6a1e0e6" - Created: 1466724217 - Size: 1092588 - SharedSize: 0 - Labels: {} - Containers: 1 - Containers: - - - Id: "e575172ed11dc01bfce087fb27bee502db149e1a0fad7c296ad300bbff178148" - Names: - - "/top" - Image: "busybox" - ImageID: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" - Command: "top" - Created: 1472592424 - Ports: [] - SizeRootFs: 1092588 - Labels: {} - State: "exited" - Status: "Exited (0) 56 minutes ago" - HostConfig: - NetworkMode: "default" - NetworkSettings: - Networks: - bridge: - IPAMConfig: null - Links: null - Aliases: null - NetworkID: "d687bc59335f0e5c9ee8193e5612e8aee000c8c62ea170cfb99c098f95899d92" - EndpointID: "8ed5115aeaad9abb174f68dcf135b49f11daf597678315231a32ca28441dec6a" - Gateway: "172.18.0.1" - IPAddress: "172.18.0.2" - IPPrefixLen: 16 - IPv6Gateway: "" - GlobalIPv6Address: "" - GlobalIPv6PrefixLen: 0 - MacAddress: "02:42:ac:12:00:02" - Mounts: [] - Volumes: - - - Name: "my-volume" - Driver: "local" - Mountpoint: "/var/lib/docker/volumes/my-volume/_data" - Labels: null - Scope: "local" - Options: null - UsageData: - Size: 10920104 - RefCount: 2 - BuildCache: - - - ID: "hw53o5aio51xtltp5xjp8v7fx" - Parents: [] - Type: "regular" - Description: "pulled from docker.io/library/debian@sha256:234cb88d3020898631af0ccbbcca9a66ae7306ecd30c9720690858c1b007d2a0" - InUse: false - Shared: true - Size: 0 - CreatedAt: "2021-06-28T13:31:01.474619385Z" - LastUsedAt: "2021-07-07T22:02:32.738075951Z" - UsageCount: 26 - - - ID: "ndlpt0hhvkqcdfkputsk4cq9c" - Parents: ["ndlpt0hhvkqcdfkputsk4cq9c"] - Type: "regular" - Description: "mount / from exec /bin/sh -c echo 'Binary::apt::APT::Keep-Downloaded-Packages \"true\";' > /etc/apt/apt.conf.d/keep-cache" - InUse: false - Shared: true - Size: 51 - CreatedAt: "2021-06-28T13:31:03.002625487Z" - LastUsedAt: "2021-07-07T22:02:32.773909517Z" - UsageCount: 26 + ImagesDiskUsage: + $ref: "#/definitions/ImagesDiskUsage" + ContainersDiskUsage: + $ref: "#/definitions/ContainersDiskUsage" + VolumesDiskUsage: + $ref: "#/definitions/VolumesDiskUsage" + BuildCacheDiskUsage: + $ref: "#/definitions/BuildCacheDiskUsage" 500: description: "server error" schema: @@ -10446,6 +10522,12 @@ paths: items: type: "string" enum: ["container", "image", "volume", "build-cache"] + - name: "verbose" + in: "query" + description: | + Show detailed information on space usage. + type: "boolean" + default: false tags: ["System"] /images/{name}/get: get: diff --git a/api/swagger.yaml b/api/swagger.yaml index 1e8644a5c6..52e0e5c18b 100644 --- a/api/swagger.yaml +++ b/api/swagger.yaml @@ -2059,6 +2059,47 @@ definitions: x-nullable: true $ref: "#/definitions/OCIDescriptor" + ImagesDiskUsage: + type: "object" + description: | + ImagesDiskUsage represents system data usage for image resources. + properties: + ActiveImages: + description: | + Count of active images. + type: "integer" + format: "int64" + example: 1 + TotalImages: + description: | + Count of all images. + type: "integer" + format: "int64" + example: 4 + Reclaimable: + description: | + Disk space that can be reclaimed by removing unused images. + type: "integer" + format: "int64" + example: 12345678 + TotalSize: + description: | + Disk space in use by images. + type: "integer" + format: "int64" + example: 98765432 + Items: + description: | + List of image summaries. + type: "array" + x-omitempty: true + items: + x-nullable: true + x-go-type: + type: Summary + import: + package: github.com/moby/moby/api/types/image + AuthConfig: type: "object" properties: @@ -2201,6 +2242,47 @@ definitions: is set to `-1` if the reference-count is not available. x-nullable: false + VolumesDiskUsage: + type: "object" + description: | + VolumesDiskUsage represents system data usage for volume resources. + properties: + ActiveVolumes: + description: | + Count of active volumes. + type: "integer" + format: "int64" + example: 1 + TotalVolumes: + description: | + Count of all volumes. + type: "integer" + format: "int64" + example: 4 + Reclaimable: + description: | + Disk space that can be reclaimed by removing inactive volumes. + type: "integer" + format: "int64" + example: 12345678 + TotalSize: + description: | + Disk space in use by volumes. + type: "integer" + format: "int64" + example: 98765432 + Items: + description: | + List of volumes. + type: "array" + x-omitempty: true + items: + x-nullable: true + x-go-type: + type: Volume + import: + package: github.com/moby/moby/api/types/volume + VolumeCreateRequest: description: "Volume configuration" type: "object" @@ -2774,6 +2856,47 @@ definitions: type: "integer" example: 26 + BuildCacheDiskUsage: + type: "object" + description: | + BuildCacheDiskUsage represents system data usage for build cache resources. + properties: + ActiveBuildCacheRecords: + description: | + Count of active build cache records. + type: "integer" + format: "int64" + example: 1 + TotalBuildCacheRecords: + description: | + Count of all build cache records. + type: "integer" + format: "int64" + example: 4 + Reclaimable: + description: | + Disk space that can be reclaimed by removing inactive build cache records. + type: "integer" + format: "int64" + example: 12345678 + TotalSize: + description: | + Disk space in use by build cache records. + type: "integer" + format: "int64" + example: 98765432 + Items: + description: | + List of build cache records. + type: "array" + x-omitempty: true + items: + x-nullable: true + x-go-type: + type: CacheRecord + import: + package: github.com/moby/moby/api/types/build + ImageID: type: "object" description: "Image ID or Digest" @@ -5457,6 +5580,47 @@ definitions: type: "integer" example: 0 + ContainersDiskUsage: + type: "object" + description: | + ContainersDiskUsage provides system data usage information for container resources. + properties: + ActiveContainers: + description: | + Count of active containers. + type: "integer" + format: "int64" + example: 1 + TotalContainers: + description: | + Count of all containers. + type: "integer" + format: "int64" + example: 4 + Reclaimable: + description: | + Disk space that can be reclaimed by removing inactive containers. + type: "integer" + format: "int64" + example: 12345678 + TotalSize: + description: | + Disk space in use by containers. + type: "integer" + format: "int64" + example: 98765432 + Items: + description: | + List of container summaries. + type: "array" + x-omitempty: true + items: + x-nullable: true + x-go-type: + type: Summary + import: + package: github.com/moby/moby/api/types/container + Driver: description: "Driver represents a driver (network, logging, secrets)." type: "object" @@ -10336,106 +10500,14 @@ paths: type: "object" title: "SystemDataUsageResponse" properties: - LayersSize: - type: "integer" - format: "int64" - Images: - type: "array" - items: - $ref: "#/definitions/ImageSummary" - Containers: - type: "array" - items: - $ref: "#/definitions/ContainerSummary" - Volumes: - type: "array" - items: - $ref: "#/definitions/Volume" - BuildCache: - type: "array" - items: - $ref: "#/definitions/BuildCache" - example: - LayersSize: 1092588 - Images: - - - Id: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" - ParentId: "" - RepoTags: - - "busybox:latest" - RepoDigests: - - "busybox@sha256:a59906e33509d14c036c8678d687bd4eec81ed7c4b8ce907b888c607f6a1e0e6" - Created: 1466724217 - Size: 1092588 - SharedSize: 0 - Labels: {} - Containers: 1 - Containers: - - - Id: "e575172ed11dc01bfce087fb27bee502db149e1a0fad7c296ad300bbff178148" - Names: - - "/top" - Image: "busybox" - ImageID: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" - Command: "top" - Created: 1472592424 - Ports: [] - SizeRootFs: 1092588 - Labels: {} - State: "exited" - Status: "Exited (0) 56 minutes ago" - HostConfig: - NetworkMode: "default" - NetworkSettings: - Networks: - bridge: - IPAMConfig: null - Links: null - Aliases: null - NetworkID: "d687bc59335f0e5c9ee8193e5612e8aee000c8c62ea170cfb99c098f95899d92" - EndpointID: "8ed5115aeaad9abb174f68dcf135b49f11daf597678315231a32ca28441dec6a" - Gateway: "172.18.0.1" - IPAddress: "172.18.0.2" - IPPrefixLen: 16 - IPv6Gateway: "" - GlobalIPv6Address: "" - GlobalIPv6PrefixLen: 0 - MacAddress: "02:42:ac:12:00:02" - Mounts: [] - Volumes: - - - Name: "my-volume" - Driver: "local" - Mountpoint: "/var/lib/docker/volumes/my-volume/_data" - Labels: null - Scope: "local" - Options: null - UsageData: - Size: 10920104 - RefCount: 2 - BuildCache: - - - ID: "hw53o5aio51xtltp5xjp8v7fx" - Parents: [] - Type: "regular" - Description: "pulled from docker.io/library/debian@sha256:234cb88d3020898631af0ccbbcca9a66ae7306ecd30c9720690858c1b007d2a0" - InUse: false - Shared: true - Size: 0 - CreatedAt: "2021-06-28T13:31:01.474619385Z" - LastUsedAt: "2021-07-07T22:02:32.738075951Z" - UsageCount: 26 - - - ID: "ndlpt0hhvkqcdfkputsk4cq9c" - Parents: ["ndlpt0hhvkqcdfkputsk4cq9c"] - Type: "regular" - Description: "mount / from exec /bin/sh -c echo 'Binary::apt::APT::Keep-Downloaded-Packages \"true\";' > /etc/apt/apt.conf.d/keep-cache" - InUse: false - Shared: true - Size: 51 - CreatedAt: "2021-06-28T13:31:03.002625487Z" - LastUsedAt: "2021-07-07T22:02:32.773909517Z" - UsageCount: 26 + ImagesDiskUsage: + $ref: "#/definitions/ImagesDiskUsage" + ContainersDiskUsage: + $ref: "#/definitions/ContainersDiskUsage" + VolumesDiskUsage: + $ref: "#/definitions/VolumesDiskUsage" + BuildCacheDiskUsage: + $ref: "#/definitions/BuildCacheDiskUsage" 500: description: "server error" schema: @@ -10450,6 +10522,12 @@ paths: items: type: "string" enum: ["container", "image", "volume", "build-cache"] + - name: "verbose" + in: "query" + description: | + Show detailed information on space usage. + type: "boolean" + default: false tags: ["System"] /images/{name}/get: get: diff --git a/api/types/system/build_cache_disk_usage.go b/api/types/system/build_cache_disk_usage.go new file mode 100644 index 0000000000..2bf617b869 --- /dev/null +++ b/api/types/system/build_cache_disk_usage.go @@ -0,0 +1,40 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package system + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "github.com/moby/moby/api/types/build" +) + +// BuildCacheDiskUsage BuildCacheDiskUsage represents system data usage for build cache resources. +// +// swagger:model BuildCacheDiskUsage +type BuildCacheDiskUsage struct { + + // Count of active build cache records. + // + // Example: 1 + ActiveBuildCacheRecords int64 `json:"ActiveBuildCacheRecords,omitempty"` + + // List of build cache records. + // + Items []*build.CacheRecord `json:"Items,omitempty"` + + // Disk space that can be reclaimed by removing inactive build cache records. + // + // Example: 12345678 + Reclaimable int64 `json:"Reclaimable,omitempty"` + + // Count of all build cache records. + // + // Example: 4 + TotalBuildCacheRecords int64 `json:"TotalBuildCacheRecords,omitempty"` + + // Disk space in use by build cache records. + // + // Example: 98765432 + TotalSize int64 `json:"TotalSize,omitempty"` +} diff --git a/api/types/system/containers_disk_usage.go b/api/types/system/containers_disk_usage.go new file mode 100644 index 0000000000..b7eda27e48 --- /dev/null +++ b/api/types/system/containers_disk_usage.go @@ -0,0 +1,40 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package system + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "github.com/moby/moby/api/types/container" +) + +// ContainersDiskUsage ContainersDiskUsage provides system data usage information for container resources. +// +// swagger:model ContainersDiskUsage +type ContainersDiskUsage struct { + + // Count of active containers. + // + // Example: 1 + ActiveContainers int64 `json:"ActiveContainers,omitempty"` + + // List of container summaries. + // + Items []*container.Summary `json:"Items,omitempty"` + + // Disk space that can be reclaimed by removing inactive containers. + // + // Example: 12345678 + Reclaimable int64 `json:"Reclaimable,omitempty"` + + // Count of all containers. + // + // Example: 4 + TotalContainers int64 `json:"TotalContainers,omitempty"` + + // Disk space in use by containers. + // + // Example: 98765432 + TotalSize int64 `json:"TotalSize,omitempty"` +} diff --git a/api/types/system/disk_usage.go b/api/types/system/disk_usage.go index 4d3315f020..c0a2a1a5c1 100644 --- a/api/types/system/disk_usage.go +++ b/api/types/system/disk_usage.go @@ -24,9 +24,27 @@ const ( // DiskUsage contains response of Engine API: // GET "/system/df" type DiskUsage struct { - LayersSize int64 - Images []*image.Summary - Containers []*container.Summary - Volumes []*volume.Volume - BuildCache []*build.CacheRecord + LegacyDiskUsage + + ImageUsage *ImagesDiskUsage `json:"ImageUsage,omitempty"` + ContainerUsage *ContainersDiskUsage `json:"ContainerUsage,omitempty"` + VolumeUsage *VolumesDiskUsage `json:"VolumeUsage,omitempty"` + BuildCacheUsage *BuildCacheDiskUsage `json:"BuildCacheUsage,omitempty"` +} + +type LegacyDiskUsage struct { + // Deprecated: kept to maintain backwards compatibility with API < v1.52, use [ImagesDiskUsage.TotalSize] instead. + LayersSize int64 `json:"LayersSize,omitempty"` + + // Deprecated: kept to maintain backwards compatibility with API < v1.52, use [ImagesDiskUsage.Items] instead. + Images []*image.Summary `json:"Images,omitempty"` + + // Deprecated: kept to maintain backwards compatibility with API < v1.52, use [ContainersDiskUsage.Items] instead. + Containers []*container.Summary `json:"Containers,omitempty"` + + // Deprecated: kept to maintain backwards compatibility with API < v1.52, use [VolumesDiskUsage.Items] instead. + Volumes []*volume.Volume `json:"Volumes,omitempty"` + + // Deprecated: kept to maintain backwards compatibility with API < v1.52, use [BuildCacheDiskUsage.Items] instead. + BuildCache []*build.CacheRecord `json:"BuildCache,omitempty"` } diff --git a/api/types/system/images_disk_usage.go b/api/types/system/images_disk_usage.go new file mode 100644 index 0000000000..7ebe1986c9 --- /dev/null +++ b/api/types/system/images_disk_usage.go @@ -0,0 +1,40 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package system + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "github.com/moby/moby/api/types/image" +) + +// ImagesDiskUsage ImagesDiskUsage represents system data usage for image resources. +// +// swagger:model ImagesDiskUsage +type ImagesDiskUsage struct { + + // Count of active images. + // + // Example: 1 + ActiveImages int64 `json:"ActiveImages,omitempty"` + + // List of image summaries. + // + Items []*image.Summary `json:"Items,omitempty"` + + // Disk space that can be reclaimed by removing unused images. + // + // Example: 12345678 + Reclaimable int64 `json:"Reclaimable,omitempty"` + + // Count of all images. + // + // Example: 4 + TotalImages int64 `json:"TotalImages,omitempty"` + + // Disk space in use by images. + // + // Example: 98765432 + TotalSize int64 `json:"TotalSize,omitempty"` +} diff --git a/api/types/system/volumes_disk_usage.go b/api/types/system/volumes_disk_usage.go new file mode 100644 index 0000000000..56e36aa83f --- /dev/null +++ b/api/types/system/volumes_disk_usage.go @@ -0,0 +1,40 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package system + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "github.com/moby/moby/api/types/volume" +) + +// VolumesDiskUsage VolumesDiskUsage represents system data usage for volume resources. +// +// swagger:model VolumesDiskUsage +type VolumesDiskUsage struct { + + // Count of active volumes. + // + // Example: 1 + ActiveVolumes int64 `json:"ActiveVolumes,omitempty"` + + // List of volumes. + // + Items []*volume.Volume `json:"Items,omitempty"` + + // Disk space that can be reclaimed by removing inactive volumes. + // + // Example: 12345678 + Reclaimable int64 `json:"Reclaimable,omitempty"` + + // Disk space in use by volumes. + // + // Example: 98765432 + TotalSize int64 `json:"TotalSize,omitempty"` + + // Count of all volumes. + // + // Example: 4 + TotalVolumes int64 `json:"TotalVolumes,omitempty"` +} diff --git a/client/client_interfaces.go b/client/client_interfaces.go index 9750076f00..c40852178f 100644 --- a/client/client_interfaces.go +++ b/client/client_interfaces.go @@ -4,8 +4,6 @@ import ( "context" "io" "net" - - "github.com/moby/moby/api/types/system" ) // APIClient is an interface that clients that talk with a docker server must implement. @@ -173,7 +171,7 @@ type SystemAPIClient interface { Events(ctx context.Context, options EventsListOptions) EventsResult Info(ctx context.Context, options InfoOptions) (SystemInfoResult, error) RegistryLogin(ctx context.Context, auth RegistryLoginOptions) (RegistryLoginResult, error) - DiskUsage(ctx context.Context, options DiskUsageOptions) (system.DiskUsage, error) + DiskUsage(ctx context.Context, options DiskUsageOptions) (DiskUsageResult, error) Ping(ctx context.Context, options PingOptions) (PingResult, error) } diff --git a/client/client_mock_test.go b/client/client_mock_test.go index 552fe92a0d..657bf11e2b 100644 --- a/client/client_mock_test.go +++ b/client/client_mock_test.go @@ -29,6 +29,17 @@ func assertRequest(req *http.Request, expMethod string, expectedPath string) err return nil } +func assertRequestWithQuery(req *http.Request, expMethod string, expectedPath string, expectedQuery string) error { + if err := assertRequest(req, expMethod, expectedPath); err != nil { + return err + } + q := req.URL.Query().Encode() + if q != expectedQuery { + return fmt.Errorf("expected query '%s', got '%s'", expectedQuery, q) + } + return nil +} + // ensureBody makes sure the response has a Body, using [http.NoBody] if // none is present, and returns it as a testRoundTripper. func ensureBody(f func(req *http.Request) (*http.Response, error)) testRoundTripper { diff --git a/client/system_disk_usage.go b/client/system_disk_usage.go index 6f78952cbd..d8cef475b3 100644 --- a/client/system_disk_usage.go +++ b/client/system_disk_usage.go @@ -6,28 +6,248 @@ import ( "fmt" "net/url" + "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" ) -// DiskUsage requests the current data usage from the daemon -func (cli *Client) DiskUsage(ctx context.Context, options DiskUsageOptions) (system.DiskUsage, error) { - var query url.Values - if len(options.Types) > 0 { - query = url.Values{} - for _, t := range options.Types { - query.Add("type", string(t)) +// 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 { + // ActiveContainers is the number of active containers. + ActiveContainers int64 + + // TotalContainers is the total number of containers. + TotalContainers 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 { + // ActiveImages is the number of active images. + ActiveImages int64 + + // TotalImages is the total number of images. + TotalImages 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 { + // ActiveVolumes is the number of active volumes. + ActiveVolumes int64 + + // TotalVolumes is the total number of volumes. + TotalVolumes 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 { + // ActiveBuildCacheRecords is the number of active build cache records. + ActiveBuildCacheRecords int64 + + // TotalBuildCacheRecords is the total number of build cache records. + TotalBuildCacheRecords 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 system.DiskUsage{}, err + return DiskUsageResult{}, err } var du system.DiskUsage if err := json.NewDecoder(resp.Body).Decode(&du); err != nil { - return system.DiskUsage{}, fmt.Errorf("Error retrieving disk usage: %v", err) + return DiskUsageResult{}, fmt.Errorf("Error retrieving disk usage: %v", err) } - return du, nil + + var ( + r DiskUsageResult + imagesFrom = []*image.Summary{} + containersFrom = []*container.Summary{} + volumesFrom = []*volume.Volume{} + buildCacheFrom = []*build.CacheRecord{} + ) + + if du.ImageUsage != nil { + r.Images = ImagesDiskUsage{ + ActiveImages: du.ImageUsage.ActiveImages, + Reclaimable: du.ImageUsage.Reclaimable, + TotalImages: du.ImageUsage.TotalImages, + TotalSize: du.ImageUsage.TotalSize, + } + + if options.Verbose { + imagesFrom = du.ImageUsage.Items + } + } else { + // Fallback for legacy response. + r.Images = ImagesDiskUsage{ + TotalSize: du.LayersSize, + } + + if du.Images != nil && options.Verbose { + imagesFrom = du.Images + } + } + + r.Images.Items = make([]image.Summary, len(imagesFrom)) + for i, ii := range imagesFrom { + r.Images.Items[i] = *ii + } + + if du.ContainerUsage != nil { + r.Containers = ContainersDiskUsage{ + ActiveContainers: du.ContainerUsage.ActiveContainers, + Reclaimable: du.ContainerUsage.Reclaimable, + TotalContainers: du.ContainerUsage.TotalContainers, + TotalSize: du.ContainerUsage.TotalSize, + } + + if options.Verbose { + containersFrom = du.ContainerUsage.Items + } + } else if du.Containers != nil && options.Verbose { + // Fallback for legacy response. + containersFrom = du.Containers + } + + r.Containers.Items = make([]container.Summary, len(containersFrom)) + for i, c := range containersFrom { + r.Containers.Items[i] = *c + } + + if du.BuildCacheUsage != nil { + r.BuildCache = BuildCacheDiskUsage{ + ActiveBuildCacheRecords: du.BuildCacheUsage.ActiveBuildCacheRecords, + Reclaimable: du.BuildCacheUsage.Reclaimable, + TotalBuildCacheRecords: du.BuildCacheUsage.TotalBuildCacheRecords, + TotalSize: du.BuildCacheUsage.TotalSize, + } + + if options.Verbose { + buildCacheFrom = du.BuildCacheUsage.Items + } + } else if du.BuildCache != nil && options.Verbose { + // Fallback for legacy response. + buildCacheFrom = du.BuildCache + } + + r.BuildCache.Items = make([]build.CacheRecord, len(buildCacheFrom)) + for i, b := range buildCacheFrom { + r.BuildCache.Items[i] = *b + } + + if du.VolumeUsage != nil { + r.Volumes = VolumesDiskUsage{ + ActiveVolumes: du.VolumeUsage.ActiveVolumes, + Reclaimable: du.VolumeUsage.Reclaimable, + TotalSize: du.VolumeUsage.TotalSize, + TotalVolumes: du.VolumeUsage.TotalVolumes, + } + + if options.Verbose { + volumesFrom = du.VolumeUsage.Items + } + } else if du.Volumes != nil && options.Verbose { + // Fallback for legacy response. + volumesFrom = du.Volumes + } + + r.Volumes.Items = make([]volume.Volume, len(volumesFrom)) + for i, v := range volumesFrom { + r.Volumes.Items[i] = *v + } + + return r, nil } diff --git a/client/system_disk_usage_opts.go b/client/system_disk_usage_opts.go deleted file mode 100644 index e671b0cb7d..0000000000 --- a/client/system_disk_usage_opts.go +++ /dev/null @@ -1,10 +0,0 @@ -package client - -import "github.com/moby/moby/api/types/system" - -// DiskUsageOptions holds parameters for system disk usage query. -type DiskUsageOptions struct { - // Types specifies what object types to include in the response. If empty, - // all object types are returned. - Types []system.DiskUsageObject -} diff --git a/client/system_disk_usage_test.go b/client/system_disk_usage_test.go index 9991247450..ac1ae09b48 100644 --- a/client/system_disk_usage_test.go +++ b/client/system_disk_usage_test.go @@ -2,10 +2,12 @@ package client import ( "context" + "fmt" "net/http" "testing" cerrdefs "github.com/containerd/errdefs" + "github.com/moby/moby/api/types/image" "github.com/moby/moby/api/types/system" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" @@ -26,13 +28,129 @@ func TestDiskUsage(t *testing.T) { } return mockJSONResponse(http.StatusOK, nil, system.DiskUsage{ - LayersSize: int64(100), - Images: nil, - Containers: nil, - Volumes: nil, + ImageUsage: &system.ImagesDiskUsage{ + ActiveImages: 0, + TotalImages: 0, + Reclaimable: 0, + TotalSize: 4096, + Items: []*image.Summary{}, + }, })(req) })) assert.NilError(t, err) - _, err = client.DiskUsage(context.Background(), DiskUsageOptions{}) + + du, err := client.DiskUsage(context.Background(), DiskUsageOptions{}) assert.NilError(t, err) + assert.Equal(t, du.Images.ActiveImages, int64(0)) + assert.Equal(t, du.Images.TotalImages, int64(0)) + assert.Equal(t, du.Images.Reclaimable, int64(0)) + assert.Equal(t, du.Images.TotalSize, int64(4096)) + assert.Equal(t, len(du.Images.Items), 0) +} + +func TestDiskUsageWithOptions(t *testing.T) { + const expectedURL = "/system/df" + + tests := []struct { + options DiskUsageOptions + expectedQuery string + }{ + { + options: DiskUsageOptions{ + Containers: true, + }, + expectedQuery: "type=container", + }, + { + options: DiskUsageOptions{ + Images: true, + }, + expectedQuery: "type=image", + }, + { + options: DiskUsageOptions{ + Volumes: true, + }, + expectedQuery: "type=volume", + }, + { + options: DiskUsageOptions{ + BuildCache: true, + }, + expectedQuery: "type=build-cache", + }, + { + options: DiskUsageOptions{ + Containers: true, + Images: true, + }, + expectedQuery: "type=container&type=image", + }, + { + options: DiskUsageOptions{ + Containers: true, + Images: true, + Volumes: true, + BuildCache: true, + }, + expectedQuery: "type=container&type=image&type=volume&type=build-cache", + }, + { + options: DiskUsageOptions{ + Containers: true, + Verbose: true, + }, + expectedQuery: "type=container&verbose=1", + }, + { + options: DiskUsageOptions{ + Containers: true, + Images: true, + Volumes: true, + BuildCache: true, + Verbose: true, + }, + expectedQuery: "type=container&type=image&type=volume&type=build-cache&verbose=1", + }, + } + + for _, tt := range tests { + t.Run(fmt.Sprintf("options=%+v", tt.options), func(t *testing.T) { + client, err := NewClientWithOpts(WithMockClient(func(req *http.Request) (*http.Response, error) { + if err := assertRequestWithQuery(req, http.MethodGet, expectedURL, tt.expectedQuery); err != nil { + return nil, err + } + + return mockJSONResponse(http.StatusOK, nil, system.DiskUsage{})(req) + })) + assert.NilError(t, err) + _, err = client.DiskUsage(t.Context(), tt.options) + assert.NilError(t, err) + }) + } +} + +func TestLegacyDiskUsage(t *testing.T) { + const expectedURL = "/system/df" + client, err := NewClientWithOpts(WithMockClient(func(req *http.Request) (*http.Response, error) { + if err := assertRequest(req, http.MethodGet, expectedURL); err != nil { + return nil, err + } + + return mockJSONResponse(http.StatusOK, nil, system.DiskUsage{ + LegacyDiskUsage: system.LegacyDiskUsage{ + LayersSize: 4096, + Images: []*image.Summary{}, + }, + })(req) + })) + assert.NilError(t, err) + + du, err := client.DiskUsage(context.Background(), DiskUsageOptions{}) + assert.NilError(t, err) + assert.Equal(t, du.Images.ActiveImages, int64(0)) + assert.Equal(t, du.Images.TotalImages, int64(0)) + assert.Equal(t, du.Images.Reclaimable, int64(0)) + assert.Equal(t, du.Images.TotalSize, int64(4096)) + assert.Equal(t, len(du.Images.Items), 0) } diff --git a/daemon/daemon.go b/daemon/daemon.go index ce06f8e952..efeb7019ce 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -34,7 +34,6 @@ import ( "github.com/moby/buildkit/util/tracing" "github.com/moby/locker" containertypes "github.com/moby/moby/api/types/container" - imagetypes "github.com/moby/moby/api/types/image" networktypes "github.com/moby/moby/api/types/network" registrytypes "github.com/moby/moby/api/types/registry" "github.com/moby/moby/api/types/swarm" @@ -134,7 +133,7 @@ type Daemon struct { seccompProfilePath string usageContainers singleflight.Group[struct{}, *backend.ContainerDiskUsage] - usageImages singleflight.Group[struct{}, []*imagetypes.Summary] + usageImages singleflight.Group[struct{}, *backend.ImageDiskUsage] usageVolumes singleflight.Group[struct{}, *backend.VolumeDiskUsage] usageLayer singleflight.Group[struct{}, int64] diff --git a/daemon/disk_usage.go b/daemon/disk_usage.go index 26476d21bc..7e09f73531 100644 --- a/daemon/disk_usage.go +++ b/daemon/disk_usage.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/moby/moby/api/types/container" - "github.com/moby/moby/api/types/image" "github.com/moby/moby/v2/daemon/internal/filters" "github.com/moby/moby/v2/daemon/server/backend" "github.com/moby/moby/v2/daemon/server/imagebackend" @@ -15,7 +14,7 @@ import ( // containerDiskUsage obtains information about container data disk usage // and makes sure that only one calculation is performed at the same time. -func (daemon *Daemon) containerDiskUsage(ctx context.Context) (*backend.ContainerDiskUsage, error) { +func (daemon *Daemon) containerDiskUsage(ctx context.Context, verbose bool) (*backend.ContainerDiskUsage, error) { res, _, err := daemon.usageContainers.Do(ctx, struct{}{}, func(ctx context.Context) (*backend.ContainerDiskUsage, error) { // Retrieve container list containers, err := daemon.Containers(ctx, &backend.ContainerListOptions{ @@ -38,13 +37,23 @@ func (daemon *Daemon) containerDiskUsage(ctx context.Context) (*backend.Containe ctr.State == container.StateRestarting } - du := &backend.ContainerDiskUsage{Items: containers} - for _, ctr := range du.Items { + activeCount := int64(len(containers)) + + du := &backend.ContainerDiskUsage{TotalCount: activeCount} + for _, ctr := range containers { du.TotalSize += ctr.SizeRw if !isActive(ctr) { du.Reclaimable += ctr.SizeRw + activeCount-- } } + + du.ActiveCount = activeCount + + if verbose { + du.Items = containers + } + return du, nil }) return res, err @@ -52,54 +61,83 @@ func (daemon *Daemon) containerDiskUsage(ctx context.Context) (*backend.Containe // imageDiskUsage obtains information about image data disk usage from image service // and makes sure that only one calculation is performed at the same time. -func (daemon *Daemon) imageDiskUsage(ctx context.Context) ([]*image.Summary, error) { - imgs, _, err := daemon.usageImages.Do(ctx, struct{}{}, func(ctx context.Context) ([]*image.Summary, error) { +func (daemon *Daemon) imageDiskUsage(ctx context.Context, verbose bool) (*backend.ImageDiskUsage, error) { + du, _, err := daemon.usageImages.Do(ctx, struct{}{}, func(ctx context.Context) (*backend.ImageDiskUsage, error) { // Get all top images with extra attributes - imgs, err := daemon.imageService.Images(ctx, imagebackend.ListOptions{ + images, err := daemon.imageService.Images(ctx, imagebackend.ListOptions{ Filters: filters.NewArgs(), SharedSize: true, }) if err != nil { return nil, errors.Wrap(err, "failed to retrieve image list") } - return imgs, nil + + reclaimable, _, 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} + for _, i := range images { + if i.Containers == 0 { + activeCount-- + if i.Size == -1 || i.SharedSize == -1 { + continue + } + reclaimable -= i.Size - i.SharedSize + } + } + + du.Reclaimable = reclaimable + du.ActiveCount = activeCount + + if verbose { + du.Items = images + } + + return du, nil }) - return imgs, err + return du, err } // localVolumesSize obtains information about volume disk usage from volumes service // and makes sure that only one size calculation is performed at the same time. -func (daemon *Daemon) localVolumesSize(ctx context.Context) (*backend.VolumeDiskUsage, error) { +func (daemon *Daemon) localVolumesSize(ctx context.Context, verbose bool) (*backend.VolumeDiskUsage, error) { volumes, _, err := daemon.usageVolumes.Do(ctx, struct{}{}, func(ctx context.Context) (*backend.VolumeDiskUsage, error) { volumes, err := daemon.volumes.LocalVolumesSize(ctx) if err != nil { return nil, err } - du := &backend.VolumeDiskUsage{Items: volumes} - for _, v := range du.Items { + activeCount := int64(len(volumes)) + + du := &backend.VolumeDiskUsage{TotalCount: activeCount} + for _, v := range volumes { if v.UsageData.Size != -1 { if v.UsageData.RefCount == 0 { du.Reclaimable += v.UsageData.Size + activeCount-- } du.TotalSize += v.UsageData.Size } } + + du.ActiveCount = activeCount + + if verbose { + du.Items = volumes + } + return du, nil }) return volumes, err } -// layerDiskUsage obtains information about layer disk usage from image service -// and makes sure that only one size calculation is performed at the same time. -func (daemon *Daemon) layerDiskUsage(ctx context.Context) (int64, error) { - usage, _, err := daemon.usageLayer.Do(ctx, struct{}{}, func(ctx context.Context) (usage int64, err error) { - return daemon.imageService.ImageDiskUsage(ctx) - }) - return usage, err -} - // SystemDiskUsage returns information about the daemon data disk usage. // Callers must not mutate contents of the returned fields. func (daemon *Daemon) SystemDiskUsage(ctx context.Context, opts backend.DiskUsageOptions) (*backend.DiskUsage, error) { @@ -108,29 +146,21 @@ func (daemon *Daemon) SystemDiskUsage(ctx context.Context, opts backend.DiskUsag du := &backend.DiskUsage{} if opts.Containers { eg.Go(func() (err error) { - du.Containers, err = daemon.containerDiskUsage(ctx) + du.Containers, err = daemon.containerDiskUsage(ctx, opts.Verbose) return err }) } - var ( - layersSize int64 - images []*image.Summary - ) if opts.Images { eg.Go(func() (err error) { - images, err = daemon.imageDiskUsage(ctx) - return err - }) - eg.Go(func() (err error) { - layersSize, err = daemon.layerDiskUsage(ctx) + du.Images, err = daemon.imageDiskUsage(ctx, opts.Verbose) return err }) } if opts.Volumes { eg.Go(func() (err error) { - du.Volumes, err = daemon.localVolumesSize(ctx) + du.Volumes, err = daemon.localVolumesSize(ctx, opts.Verbose) return err }) } @@ -139,22 +169,5 @@ func (daemon *Daemon) SystemDiskUsage(ctx context.Context, opts backend.DiskUsag return nil, err } - if opts.Images { - reclaimable := layersSize - for _, i := range images { - if i.Containers != 0 { - if i.Size == -1 || i.SharedSize == -1 { - continue - } - reclaimable -= i.Size - i.SharedSize - } - } - - du.Images = &backend.ImageDiskUsage{ - TotalSize: layersSize, - Reclaimable: reclaimable, - Items: images, - } - } return du, nil } diff --git a/daemon/server/backend/disk_usage.go b/daemon/server/backend/disk_usage.go index 6edb47c4f5..1798e2c6aa 100644 --- a/daemon/server/backend/disk_usage.go +++ b/daemon/server/backend/disk_usage.go @@ -17,6 +17,9 @@ type DiskUsageOptions struct { // Volumes controls whether volume disk usage should be computed. Volumes bool + + // Verbose indicates whether to include detailed information. + Verbose bool } // DiskUsage contains the information returned by the backend for the @@ -30,6 +33,8 @@ type DiskUsage struct { // BuildCacheDiskUsage contains disk usage for the build cache. type BuildCacheDiskUsage struct { + ActiveCount int64 + TotalCount int64 TotalSize int64 Reclaimable int64 Items []*build.CacheRecord @@ -37,6 +42,8 @@ type BuildCacheDiskUsage struct { // ContainerDiskUsage contains disk usage for containers. type ContainerDiskUsage struct { + ActiveCount int64 + TotalCount int64 TotalSize int64 Reclaimable int64 Items []*container.Summary @@ -44,6 +51,8 @@ type ContainerDiskUsage struct { // ImageDiskUsage contains disk usage for images. type ImageDiskUsage struct { + ActiveCount int64 + TotalCount int64 TotalSize int64 Reclaimable int64 Items []*image.Summary @@ -51,6 +60,8 @@ type ImageDiskUsage struct { // VolumeDiskUsage contains disk usage for volumes. type VolumeDiskUsage struct { + ActiveCount int64 + TotalCount int64 TotalSize int64 Reclaimable int64 Items []*volume.Volume diff --git a/daemon/server/router/system/system_routes.go b/daemon/server/router/system/system_routes.go index a57e5d1e72..a91f022777 100644 --- a/daemon/server/router/system/system_routes.go +++ b/daemon/server/router/system/system_routes.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "net/http" + "strconv" "time" "github.com/containerd/log" @@ -161,6 +162,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)} + } + } else { + // In versions prior to 1.52, legacy fields were always included. + legacyFields, verbose = true, true + } + eg, ctx := errgroup.WithContext(ctx) var systemDiskUsage *backend.DiskUsage @@ -171,6 +187,7 @@ func (s *systemRouter) getDiskUsage(ctx context.Context, w http.ResponseWriter, Containers: getContainers, Images: getImages, Volumes: getVolumes, + Verbose: verbose, }) return err }) @@ -197,40 +214,80 @@ func (s *systemRouter) getDiskUsage(ctx context.Context, w http.ResponseWriter, return err } - var builderSize int64 - if versions.LessThan(version, "1.42") { - for _, b := range buildCache { - builderSize += b.Size - } - } - - du := backend.DiskUsage{} - if getBuildCache { - du.BuildCache = &backend.BuildCacheDiskUsage{ - TotalSize: builderSize, - Items: buildCache, - } - } - if systemDiskUsage != nil { - du.Images = systemDiskUsage.Images - du.Containers = systemDiskUsage.Containers - du.Volumes = systemDiskUsage.Volumes - } - - // Use the old struct for the API return value. var v system.DiskUsage - if du.Images != nil { - v.LayersSize = du.Images.TotalSize - v.Images = du.Images.Items + if systemDiskUsage != nil && systemDiskUsage.Images != nil { + v.ImageUsage = &system.ImagesDiskUsage{ + ActiveImages: systemDiskUsage.Images.ActiveCount, + Reclaimable: systemDiskUsage.Images.Reclaimable, + TotalImages: systemDiskUsage.Images.TotalCount, + TotalSize: systemDiskUsage.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 = systemDiskUsage.Images.Items //nolint: staticcheck,SA1019: v.Images is deprecated: kept to maintain backwards compatibility with API < v1.52, use [ImagesDiskUsage.Items] instead. + } else if verbose { + v.ImageUsage.Items = systemDiskUsage.Images.Items + } } - if du.Containers != nil { - v.Containers = du.Containers.Items + if systemDiskUsage != nil && systemDiskUsage.Containers != nil { + v.ContainerUsage = &system.ContainersDiskUsage{ + ActiveContainers: systemDiskUsage.Containers.ActiveCount, + Reclaimable: systemDiskUsage.Containers.Reclaimable, + TotalContainers: systemDiskUsage.Containers.TotalCount, + TotalSize: systemDiskUsage.Containers.TotalSize, + } + + if legacyFields { + v.Containers = systemDiskUsage.Containers.Items //nolint: staticcheck,SA1019: v.Containers is deprecated: kept to maintain backwards compatibility with API < v1.52, use [ContainersDiskUsage.Items] instead. + } else if verbose { + v.ContainerUsage.Items = systemDiskUsage.Containers.Items + } } - if du.Volumes != nil { - v.Volumes = du.Volumes.Items + if systemDiskUsage != nil && systemDiskUsage.Volumes != nil { + v.VolumeUsage = &system.VolumesDiskUsage{ + ActiveVolumes: systemDiskUsage.Volumes.ActiveCount, + TotalSize: systemDiskUsage.Volumes.TotalSize, + Reclaimable: systemDiskUsage.Volumes.Reclaimable, + TotalVolumes: systemDiskUsage.Volumes.TotalCount, + } + + if legacyFields { + v.Volumes = systemDiskUsage.Volumes.Items //nolint: staticcheck,SA1019: v.Volumes is deprecated: kept to maintain backwards compatibility with API < v1.52, use [VolumesDiskUsage.Items] instead. + } else if verbose { + v.VolumeUsage.Items = systemDiskUsage.Volumes.Items + } } - if du.BuildCache != nil { - v.BuildCache = du.BuildCache.Items + if getBuildCache { + v.BuildCacheUsage = &system.BuildCacheDiskUsage{ + TotalBuildCacheRecords: int64(len(buildCache)), + } + + activeCount := v.BuildCacheUsage.TotalBuildCacheRecords + 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.ActiveBuildCacheRecords = activeCount + v.BuildCacheUsage.TotalSize = totalSize + v.BuildCacheUsage.Reclaimable = reclaimable + + if legacyFields { + v.BuildCache = buildCache //nolint: staticcheck,SA1019: v.BuildCache is deprecated: kept to maintain backwards compatibility with API < v1.52, use [BuildCacheDiskUsage.Items] instead. + } else if verbose { + v.BuildCacheUsage.Items = buildCache + } } return httputils.WriteJSON(w, http.StatusOK, v) } diff --git a/hack/generate-swagger-api.sh b/hack/generate-swagger-api.sh index 2e4661dd0c..aa59408cef 100755 --- a/hack/generate-swagger-api.sh +++ b/hack/generate-swagger-api.sh @@ -95,6 +95,13 @@ generate_model types/storage <<- 'EOT' Storage EOT +generate_model types/system <<- 'EOT' + BuildCacheDiskUsage + ContainersDiskUsage + ImagesDiskUsage + VolumesDiskUsage +EOT + generate_model types/swarm <<- 'EOT' ServiceCreateResponse ServiceUpdateResponse diff --git a/integration/system/disk_usage_test.go b/integration/system/disk_usage_test.go index 1b1f063802..0023ef7e98 100644 --- a/integration/system/disk_usage_test.go +++ b/integration/system/disk_usage_test.go @@ -8,7 +8,6 @@ import ( "github.com/moby/moby/api/types/build" containertypes "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" "github.com/moby/moby/v2/integration/internal/container" @@ -30,78 +29,110 @@ func TestDiskUsage(t *testing.T) { defer d.Stop(t) apiClient := d.NewClientT(t) - var stepDU system.DiskUsage + var stepDU client.DiskUsageResult for _, step := range []struct { doc string - next func(t *testing.T, prev system.DiskUsage) system.DiskUsage + next func(t *testing.T, prev client.DiskUsageResult) client.DiskUsageResult }{ { doc: "empty", - next: func(t *testing.T, _ system.DiskUsage) system.DiskUsage { - du, err := apiClient.DiskUsage(ctx, client.DiskUsageOptions{}) + next: func(t *testing.T, _ client.DiskUsageResult) client.DiskUsageResult { + du, err := apiClient.DiskUsage(ctx, client.DiskUsageOptions{ + Images: true, + Containers: true, + BuildCache: true, + Volumes: true, + Verbose: true, + }) assert.NilError(t, err) expectedLayersSize := int64(0) // TODO: Investigate https://github.com/moby/moby/issues/47119 // Make 4096 (block size) also a valid value for zero usage. if testEnv.UsingSnapshotter() && testEnv.IsRootless() { - if du.LayersSize == 4096 { - expectedLayersSize = du.LayersSize + if du.Images.TotalSize == 4096 { + expectedLayersSize = 4096 } } - assert.DeepEqual(t, du, system.DiskUsage{ - LayersSize: expectedLayersSize, - Images: []*image.Summary{}, - Containers: []*containertypes.Summary{}, - Volumes: []*volume.Volume{}, - BuildCache: []*build.CacheRecord{}, + assert.DeepEqual(t, du, client.DiskUsageResult{ + Containers: client.ContainersDiskUsage{ + Items: []containertypes.Summary{}, + }, + Images: client.ImagesDiskUsage{ + TotalSize: expectedLayersSize, + Items: []image.Summary{}, + }, + BuildCache: client.BuildCacheDiskUsage{ + Items: []build.CacheRecord{}, + }, + Volumes: client.VolumesDiskUsage{ + Items: []volume.Volume{}, + }, }) return du }, }, { doc: "after LoadBusybox", - next: func(t *testing.T, _ system.DiskUsage) system.DiskUsage { + next: func(t *testing.T, _ client.DiskUsageResult) client.DiskUsageResult { d.LoadBusybox(ctx, t) - du, err := apiClient.DiskUsage(ctx, client.DiskUsageOptions{}) + du, err := apiClient.DiskUsage(ctx, client.DiskUsageOptions{ + Images: true, + Containers: true, + BuildCache: true, + Volumes: true, + Verbose: true, + }) assert.NilError(t, err) - assert.Assert(t, du.LayersSize > 0) - assert.Equal(t, len(du.Images), 1) - assert.Equal(t, len(du.Images[0].RepoTags), 1) - assert.Check(t, is.Equal(du.Images[0].RepoTags[0], "busybox:latest")) + + assert.Assert(t, du.Images.TotalSize > 0) + assert.Equal(t, len(du.Images.Items), 1) + assert.Equal(t, len(du.Images.Items[0].RepoTags), 1) + assert.Check(t, is.Equal(du.Images.Items[0].RepoTags[0], "busybox:latest")) // Image size is layer size + content size. Content size is included in layers size. - assert.Equal(t, du.Images[0].Size, du.LayersSize) + assert.Equal(t, du.Images.Items[0].Size, du.Images.TotalSize) return du }, }, { doc: "after container.Run", - next: func(t *testing.T, prev system.DiskUsage) system.DiskUsage { + next: func(t *testing.T, prev client.DiskUsageResult) client.DiskUsageResult { cID := container.Run(ctx, t, apiClient) - du, err := apiClient.DiskUsage(ctx, client.DiskUsageOptions{}) + du, err := apiClient.DiskUsage(ctx, client.DiskUsageOptions{ + Images: true, + Containers: true, + BuildCache: true, + Volumes: true, + Verbose: true, + }) assert.NilError(t, err) - assert.Equal(t, len(du.Containers), 1) - assert.Equal(t, len(du.Containers[0].Names), 1) - assert.Assert(t, len(prev.Images) > 0) - assert.Check(t, du.Containers[0].Created >= prev.Images[0].Created) + + assert.Equal(t, du.Containers.ActiveContainers, int64(1)) + assert.Equal(t, du.Containers.TotalContainers, int64(1)) + assert.Equal(t, len(du.Containers.Items), 1) + assert.Equal(t, len(du.Containers.Items[0].Names), 1) + assert.Assert(t, len(prev.Images.Items) > 0) + assert.Check(t, du.Containers.Items[0].Created >= prev.Images.Items[0].Created) // Additional container layer could add to the size - assert.Check(t, du.LayersSize >= prev.LayersSize) + assert.Check(t, du.Images.TotalSize >= prev.Images.TotalSize) - assert.Equal(t, len(du.Images), 1) - assert.Equal(t, du.Images[0].Containers, prev.Images[0].Containers+1) + assert.Equal(t, du.Images.ActiveImages, int64(1)) + assert.Equal(t, du.Images.TotalImages, int64(1)) + assert.Equal(t, len(du.Images.Items), 1) + assert.Equal(t, du.Images.Items[0].Containers, prev.Images.Items[0].Containers+1) - assert.Check(t, is.Equal(du.Containers[0].ID, cID)) - assert.Check(t, is.Equal(du.Containers[0].Image, "busybox")) - assert.Check(t, is.Equal(du.Containers[0].ImageID, prev.Images[0].ID)) + assert.Check(t, is.Equal(du.Containers.Items[0].ID, cID)) + assert.Check(t, is.Equal(du.Containers.Items[0].Image, "busybox")) + assert.Check(t, is.Equal(du.Containers.Items[0].ImageID, prev.Images.Items[0].ID)) // ImageManifestDescriptor should NOT be populated. - assert.Check(t, is.Nil(du.Containers[0].ImageManifestDescriptor)) + assert.Check(t, is.Nil(du.Containers.Items[0].ImageManifestDescriptor)) return du }, @@ -114,143 +145,153 @@ func TestDiskUsage(t *testing.T) { for _, tc := range []struct { doc string options client.DiskUsageOptions - expected system.DiskUsage + expected client.DiskUsageResult }{ { doc: "container types", options: client.DiskUsageOptions{ - Types: []system.DiskUsageObject{ - system.ContainerObject, - }, + Containers: true, + Verbose: true, }, - expected: system.DiskUsage{ + expected: client.DiskUsageResult{ Containers: stepDU.Containers, + Images: client.ImagesDiskUsage{Items: []image.Summary{}}, + BuildCache: client.BuildCacheDiskUsage{Items: []build.CacheRecord{}}, + Volumes: client.VolumesDiskUsage{Items: []volume.Volume{}}, }, }, { doc: "image types", options: client.DiskUsageOptions{ - Types: []system.DiskUsageObject{ - system.ImageObject, - }, + Images: true, + Verbose: true, }, - expected: system.DiskUsage{ - LayersSize: stepDU.LayersSize, + expected: client.DiskUsageResult{ + Containers: client.ContainersDiskUsage{Items: []containertypes.Summary{}}, Images: stepDU.Images, + BuildCache: client.BuildCacheDiskUsage{Items: []build.CacheRecord{}}, + Volumes: client.VolumesDiskUsage{Items: []volume.Volume{}}, }, }, { doc: "volume types", options: client.DiskUsageOptions{ - Types: []system.DiskUsageObject{ - system.VolumeObject, - }, + Volumes: true, + Verbose: true, }, - expected: system.DiskUsage{ - Volumes: stepDU.Volumes, + expected: client.DiskUsageResult{ + Containers: client.ContainersDiskUsage{Items: []containertypes.Summary{}}, + Images: client.ImagesDiskUsage{Items: []image.Summary{}}, + BuildCache: client.BuildCacheDiskUsage{Items: []build.CacheRecord{}}, + Volumes: stepDU.Volumes, }, }, { doc: "build-cache types", options: client.DiskUsageOptions{ - Types: []system.DiskUsageObject{ - system.BuildCacheObject, - }, + BuildCache: true, + Verbose: true, }, - expected: system.DiskUsage{ + expected: client.DiskUsageResult{ + Containers: client.ContainersDiskUsage{Items: []containertypes.Summary{}}, + Images: client.ImagesDiskUsage{Items: []image.Summary{}}, BuildCache: stepDU.BuildCache, + Volumes: client.VolumesDiskUsage{Items: []volume.Volume{}}, }, }, { doc: "container, volume types", options: client.DiskUsageOptions{ - Types: []system.DiskUsageObject{ - system.ContainerObject, - system.VolumeObject, - }, + Containers: true, + Volumes: true, + Verbose: true, }, - expected: system.DiskUsage{ + expected: client.DiskUsageResult{ Containers: stepDU.Containers, + Images: client.ImagesDiskUsage{Items: []image.Summary{}}, + BuildCache: client.BuildCacheDiskUsage{Items: []build.CacheRecord{}}, Volumes: stepDU.Volumes, }, }, { doc: "image, build-cache types", options: client.DiskUsageOptions{ - Types: []system.DiskUsageObject{ - system.ImageObject, - system.BuildCacheObject, - }, + Images: true, + BuildCache: true, + Verbose: true, }, - expected: system.DiskUsage{ - LayersSize: stepDU.LayersSize, + expected: client.DiskUsageResult{ + Containers: client.ContainersDiskUsage{Items: []containertypes.Summary{}}, Images: stepDU.Images, BuildCache: stepDU.BuildCache, + Volumes: client.VolumesDiskUsage{Items: []volume.Volume{}}, }, }, { doc: "container, volume, build-cache types", options: client.DiskUsageOptions{ - Types: []system.DiskUsageObject{ - system.ContainerObject, - system.VolumeObject, - system.BuildCacheObject, - }, + Containers: true, + BuildCache: true, + Volumes: true, + Verbose: true, }, - expected: system.DiskUsage{ + expected: client.DiskUsageResult{ Containers: stepDU.Containers, - Volumes: stepDU.Volumes, + Images: client.ImagesDiskUsage{ + Items: []image.Summary{}, + }, BuildCache: stepDU.BuildCache, + Volumes: stepDU.Volumes, }, }, { doc: "image, volume, build-cache types", options: client.DiskUsageOptions{ - Types: []system.DiskUsageObject{ - system.ImageObject, - system.VolumeObject, - system.BuildCacheObject, - }, + Images: true, + BuildCache: true, + Volumes: true, + Verbose: true, }, - expected: system.DiskUsage{ - LayersSize: stepDU.LayersSize, + expected: client.DiskUsageResult{ + Containers: client.ContainersDiskUsage{ + Items: []containertypes.Summary{}, + }, Images: stepDU.Images, - Volumes: stepDU.Volumes, BuildCache: stepDU.BuildCache, + Volumes: stepDU.Volumes, }, }, { doc: "container, image, volume types", options: client.DiskUsageOptions{ - Types: []system.DiskUsageObject{ - system.ContainerObject, - system.ImageObject, - system.VolumeObject, - }, + Containers: true, + Images: true, + Volumes: true, + Verbose: true, }, - expected: system.DiskUsage{ - LayersSize: stepDU.LayersSize, + expected: client.DiskUsageResult{ Containers: stepDU.Containers, Images: stepDU.Images, - Volumes: stepDU.Volumes, + BuildCache: client.BuildCacheDiskUsage{ + Items: []build.CacheRecord{}, + }, + Volumes: stepDU.Volumes, }, }, { doc: "container, image, volume, build-cache types", options: client.DiskUsageOptions{ - Types: []system.DiskUsageObject{ - system.ContainerObject, - system.ImageObject, - system.VolumeObject, - system.BuildCacheObject, - }, + Containers: true, + Images: true, + BuildCache: true, + Volumes: true, + Verbose: true, }, - expected: system.DiskUsage{ - LayersSize: stepDU.LayersSize, + expected: client.DiskUsageResult{ Containers: stepDU.Containers, Images: stepDU.Images, - Volumes: stepDU.Volumes, BuildCache: stepDU.BuildCache, + Volumes: stepDU.Volumes, }, }, } { diff --git a/vendor/github.com/moby/moby/api/types/system/build_cache_disk_usage.go b/vendor/github.com/moby/moby/api/types/system/build_cache_disk_usage.go new file mode 100644 index 0000000000..2bf617b869 --- /dev/null +++ b/vendor/github.com/moby/moby/api/types/system/build_cache_disk_usage.go @@ -0,0 +1,40 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package system + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "github.com/moby/moby/api/types/build" +) + +// BuildCacheDiskUsage BuildCacheDiskUsage represents system data usage for build cache resources. +// +// swagger:model BuildCacheDiskUsage +type BuildCacheDiskUsage struct { + + // Count of active build cache records. + // + // Example: 1 + ActiveBuildCacheRecords int64 `json:"ActiveBuildCacheRecords,omitempty"` + + // List of build cache records. + // + Items []*build.CacheRecord `json:"Items,omitempty"` + + // Disk space that can be reclaimed by removing inactive build cache records. + // + // Example: 12345678 + Reclaimable int64 `json:"Reclaimable,omitempty"` + + // Count of all build cache records. + // + // Example: 4 + TotalBuildCacheRecords int64 `json:"TotalBuildCacheRecords,omitempty"` + + // Disk space in use by build cache records. + // + // Example: 98765432 + TotalSize int64 `json:"TotalSize,omitempty"` +} diff --git a/vendor/github.com/moby/moby/api/types/system/containers_disk_usage.go b/vendor/github.com/moby/moby/api/types/system/containers_disk_usage.go new file mode 100644 index 0000000000..b7eda27e48 --- /dev/null +++ b/vendor/github.com/moby/moby/api/types/system/containers_disk_usage.go @@ -0,0 +1,40 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package system + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "github.com/moby/moby/api/types/container" +) + +// ContainersDiskUsage ContainersDiskUsage provides system data usage information for container resources. +// +// swagger:model ContainersDiskUsage +type ContainersDiskUsage struct { + + // Count of active containers. + // + // Example: 1 + ActiveContainers int64 `json:"ActiveContainers,omitempty"` + + // List of container summaries. + // + Items []*container.Summary `json:"Items,omitempty"` + + // Disk space that can be reclaimed by removing inactive containers. + // + // Example: 12345678 + Reclaimable int64 `json:"Reclaimable,omitempty"` + + // Count of all containers. + // + // Example: 4 + TotalContainers int64 `json:"TotalContainers,omitempty"` + + // Disk space in use by containers. + // + // Example: 98765432 + TotalSize int64 `json:"TotalSize,omitempty"` +} diff --git a/vendor/github.com/moby/moby/api/types/system/disk_usage.go b/vendor/github.com/moby/moby/api/types/system/disk_usage.go index 4d3315f020..c0a2a1a5c1 100644 --- a/vendor/github.com/moby/moby/api/types/system/disk_usage.go +++ b/vendor/github.com/moby/moby/api/types/system/disk_usage.go @@ -24,9 +24,27 @@ const ( // DiskUsage contains response of Engine API: // GET "/system/df" type DiskUsage struct { - LayersSize int64 - Images []*image.Summary - Containers []*container.Summary - Volumes []*volume.Volume - BuildCache []*build.CacheRecord + LegacyDiskUsage + + ImageUsage *ImagesDiskUsage `json:"ImageUsage,omitempty"` + ContainerUsage *ContainersDiskUsage `json:"ContainerUsage,omitempty"` + VolumeUsage *VolumesDiskUsage `json:"VolumeUsage,omitempty"` + BuildCacheUsage *BuildCacheDiskUsage `json:"BuildCacheUsage,omitempty"` +} + +type LegacyDiskUsage struct { + // Deprecated: kept to maintain backwards compatibility with API < v1.52, use [ImagesDiskUsage.TotalSize] instead. + LayersSize int64 `json:"LayersSize,omitempty"` + + // Deprecated: kept to maintain backwards compatibility with API < v1.52, use [ImagesDiskUsage.Items] instead. + Images []*image.Summary `json:"Images,omitempty"` + + // Deprecated: kept to maintain backwards compatibility with API < v1.52, use [ContainersDiskUsage.Items] instead. + Containers []*container.Summary `json:"Containers,omitempty"` + + // Deprecated: kept to maintain backwards compatibility with API < v1.52, use [VolumesDiskUsage.Items] instead. + Volumes []*volume.Volume `json:"Volumes,omitempty"` + + // Deprecated: kept to maintain backwards compatibility with API < v1.52, use [BuildCacheDiskUsage.Items] instead. + BuildCache []*build.CacheRecord `json:"BuildCache,omitempty"` } diff --git a/vendor/github.com/moby/moby/api/types/system/images_disk_usage.go b/vendor/github.com/moby/moby/api/types/system/images_disk_usage.go new file mode 100644 index 0000000000..7ebe1986c9 --- /dev/null +++ b/vendor/github.com/moby/moby/api/types/system/images_disk_usage.go @@ -0,0 +1,40 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package system + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "github.com/moby/moby/api/types/image" +) + +// ImagesDiskUsage ImagesDiskUsage represents system data usage for image resources. +// +// swagger:model ImagesDiskUsage +type ImagesDiskUsage struct { + + // Count of active images. + // + // Example: 1 + ActiveImages int64 `json:"ActiveImages,omitempty"` + + // List of image summaries. + // + Items []*image.Summary `json:"Items,omitempty"` + + // Disk space that can be reclaimed by removing unused images. + // + // Example: 12345678 + Reclaimable int64 `json:"Reclaimable,omitempty"` + + // Count of all images. + // + // Example: 4 + TotalImages int64 `json:"TotalImages,omitempty"` + + // Disk space in use by images. + // + // Example: 98765432 + TotalSize int64 `json:"TotalSize,omitempty"` +} diff --git a/vendor/github.com/moby/moby/api/types/system/volumes_disk_usage.go b/vendor/github.com/moby/moby/api/types/system/volumes_disk_usage.go new file mode 100644 index 0000000000..56e36aa83f --- /dev/null +++ b/vendor/github.com/moby/moby/api/types/system/volumes_disk_usage.go @@ -0,0 +1,40 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package system + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "github.com/moby/moby/api/types/volume" +) + +// VolumesDiskUsage VolumesDiskUsage represents system data usage for volume resources. +// +// swagger:model VolumesDiskUsage +type VolumesDiskUsage struct { + + // Count of active volumes. + // + // Example: 1 + ActiveVolumes int64 `json:"ActiveVolumes,omitempty"` + + // List of volumes. + // + Items []*volume.Volume `json:"Items,omitempty"` + + // Disk space that can be reclaimed by removing inactive volumes. + // + // Example: 12345678 + Reclaimable int64 `json:"Reclaimable,omitempty"` + + // Disk space in use by volumes. + // + // Example: 98765432 + TotalSize int64 `json:"TotalSize,omitempty"` + + // Count of all volumes. + // + // Example: 4 + TotalVolumes int64 `json:"TotalVolumes,omitempty"` +} diff --git a/vendor/github.com/moby/moby/client/client_interfaces.go b/vendor/github.com/moby/moby/client/client_interfaces.go index 9750076f00..c40852178f 100644 --- a/vendor/github.com/moby/moby/client/client_interfaces.go +++ b/vendor/github.com/moby/moby/client/client_interfaces.go @@ -4,8 +4,6 @@ import ( "context" "io" "net" - - "github.com/moby/moby/api/types/system" ) // APIClient is an interface that clients that talk with a docker server must implement. @@ -173,7 +171,7 @@ type SystemAPIClient interface { Events(ctx context.Context, options EventsListOptions) EventsResult Info(ctx context.Context, options InfoOptions) (SystemInfoResult, error) RegistryLogin(ctx context.Context, auth RegistryLoginOptions) (RegistryLoginResult, error) - DiskUsage(ctx context.Context, options DiskUsageOptions) (system.DiskUsage, error) + DiskUsage(ctx context.Context, options DiskUsageOptions) (DiskUsageResult, error) Ping(ctx context.Context, options PingOptions) (PingResult, error) } diff --git a/vendor/github.com/moby/moby/client/system_disk_usage.go b/vendor/github.com/moby/moby/client/system_disk_usage.go index 6f78952cbd..d8cef475b3 100644 --- a/vendor/github.com/moby/moby/client/system_disk_usage.go +++ b/vendor/github.com/moby/moby/client/system_disk_usage.go @@ -6,28 +6,248 @@ import ( "fmt" "net/url" + "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" ) -// DiskUsage requests the current data usage from the daemon -func (cli *Client) DiskUsage(ctx context.Context, options DiskUsageOptions) (system.DiskUsage, error) { - var query url.Values - if len(options.Types) > 0 { - query = url.Values{} - for _, t := range options.Types { - query.Add("type", string(t)) +// 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 { + // ActiveContainers is the number of active containers. + ActiveContainers int64 + + // TotalContainers is the total number of containers. + TotalContainers 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 { + // ActiveImages is the number of active images. + ActiveImages int64 + + // TotalImages is the total number of images. + TotalImages 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 { + // ActiveVolumes is the number of active volumes. + ActiveVolumes int64 + + // TotalVolumes is the total number of volumes. + TotalVolumes 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 { + // ActiveBuildCacheRecords is the number of active build cache records. + ActiveBuildCacheRecords int64 + + // TotalBuildCacheRecords is the total number of build cache records. + TotalBuildCacheRecords 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 system.DiskUsage{}, err + return DiskUsageResult{}, err } var du system.DiskUsage if err := json.NewDecoder(resp.Body).Decode(&du); err != nil { - return system.DiskUsage{}, fmt.Errorf("Error retrieving disk usage: %v", err) + return DiskUsageResult{}, fmt.Errorf("Error retrieving disk usage: %v", err) } - return du, nil + + var ( + r DiskUsageResult + imagesFrom = []*image.Summary{} + containersFrom = []*container.Summary{} + volumesFrom = []*volume.Volume{} + buildCacheFrom = []*build.CacheRecord{} + ) + + if du.ImageUsage != nil { + r.Images = ImagesDiskUsage{ + ActiveImages: du.ImageUsage.ActiveImages, + Reclaimable: du.ImageUsage.Reclaimable, + TotalImages: du.ImageUsage.TotalImages, + TotalSize: du.ImageUsage.TotalSize, + } + + if options.Verbose { + imagesFrom = du.ImageUsage.Items + } + } else { + // Fallback for legacy response. + r.Images = ImagesDiskUsage{ + TotalSize: du.LayersSize, + } + + if du.Images != nil && options.Verbose { + imagesFrom = du.Images + } + } + + r.Images.Items = make([]image.Summary, len(imagesFrom)) + for i, ii := range imagesFrom { + r.Images.Items[i] = *ii + } + + if du.ContainerUsage != nil { + r.Containers = ContainersDiskUsage{ + ActiveContainers: du.ContainerUsage.ActiveContainers, + Reclaimable: du.ContainerUsage.Reclaimable, + TotalContainers: du.ContainerUsage.TotalContainers, + TotalSize: du.ContainerUsage.TotalSize, + } + + if options.Verbose { + containersFrom = du.ContainerUsage.Items + } + } else if du.Containers != nil && options.Verbose { + // Fallback for legacy response. + containersFrom = du.Containers + } + + r.Containers.Items = make([]container.Summary, len(containersFrom)) + for i, c := range containersFrom { + r.Containers.Items[i] = *c + } + + if du.BuildCacheUsage != nil { + r.BuildCache = BuildCacheDiskUsage{ + ActiveBuildCacheRecords: du.BuildCacheUsage.ActiveBuildCacheRecords, + Reclaimable: du.BuildCacheUsage.Reclaimable, + TotalBuildCacheRecords: du.BuildCacheUsage.TotalBuildCacheRecords, + TotalSize: du.BuildCacheUsage.TotalSize, + } + + if options.Verbose { + buildCacheFrom = du.BuildCacheUsage.Items + } + } else if du.BuildCache != nil && options.Verbose { + // Fallback for legacy response. + buildCacheFrom = du.BuildCache + } + + r.BuildCache.Items = make([]build.CacheRecord, len(buildCacheFrom)) + for i, b := range buildCacheFrom { + r.BuildCache.Items[i] = *b + } + + if du.VolumeUsage != nil { + r.Volumes = VolumesDiskUsage{ + ActiveVolumes: du.VolumeUsage.ActiveVolumes, + Reclaimable: du.VolumeUsage.Reclaimable, + TotalSize: du.VolumeUsage.TotalSize, + TotalVolumes: du.VolumeUsage.TotalVolumes, + } + + if options.Verbose { + volumesFrom = du.VolumeUsage.Items + } + } else if du.Volumes != nil && options.Verbose { + // Fallback for legacy response. + volumesFrom = du.Volumes + } + + r.Volumes.Items = make([]volume.Volume, len(volumesFrom)) + for i, v := range volumesFrom { + r.Volumes.Items[i] = *v + } + + return r, nil } diff --git a/vendor/github.com/moby/moby/client/system_disk_usage_opts.go b/vendor/github.com/moby/moby/client/system_disk_usage_opts.go deleted file mode 100644 index e671b0cb7d..0000000000 --- a/vendor/github.com/moby/moby/client/system_disk_usage_opts.go +++ /dev/null @@ -1,10 +0,0 @@ -package client - -import "github.com/moby/moby/api/types/system" - -// DiskUsageOptions holds parameters for system disk usage query. -type DiskUsageOptions struct { - // Types specifies what object types to include in the response. If empty, - // all object types are returned. - Types []system.DiskUsageObject -}