Skip to content
This repository was archived by the owner on Oct 13, 2023. It is now read-only.

Commit 47ad2f3

Browse files
Roman VolosatovsthaJeztah
Roman Volosatovs
andcommitted
API,daemon: support type URL parameter to /system/df
Let clients choose object types to compute disk usage of. Signed-off-by: Roman Volosatovs <[email protected]> Co-authored-by: Sebastiaan van Stijn <[email protected]>
1 parent 12f1b3c commit 47ad2f3

File tree

11 files changed

+445
-64
lines changed

11 files changed

+445
-64
lines changed

api/server/router/system/backend.go

+13-1
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,24 @@ import (
1010
"github.com/docker/docker/api/types/swarm"
1111
)
1212

13+
// DiskUsageOptions holds parameters for system disk usage query.
14+
type DiskUsageOptions struct {
15+
// Containers controls whether container disk usage should be computed.
16+
Containers bool
17+
18+
// Images controls whether image disk usage should be computed.
19+
Images bool
20+
21+
// Volumes controls whether volume disk usage should be computed.
22+
Volumes bool
23+
}
24+
1325
// Backend is the methods that need to be implemented to provide
1426
// system specific functionality.
1527
type Backend interface {
1628
SystemInfo() *types.Info
1729
SystemVersion() types.Version
18-
SystemDiskUsage(ctx context.Context) (*types.DiskUsage, error)
30+
SystemDiskUsage(ctx context.Context, opts DiskUsageOptions) (*types.DiskUsage, error)
1931
SubscribeToEvents(since, until time.Time, ef filters.Args) ([]events.Message, chan interface{})
2032
UnsubscribeFromEvents(chan interface{})
2133
AuthenticateToRegistry(ctx context.Context, authConfig *types.AuthConfig) (string, string, error)

api/server/router/system/system_routes.go

+62-23
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
timetypes "github.com/docker/docker/api/types/time"
1717
"github.com/docker/docker/api/types/versions"
1818
"github.com/docker/docker/pkg/ioutils"
19-
pkgerrors "github.com/pkg/errors"
19+
"github.com/pkg/errors"
2020
"github.com/sirupsen/logrus"
2121
"golang.org/x/sync/errgroup"
2222
)
@@ -90,44 +90,83 @@ func (s *systemRouter) getVersion(ctx context.Context, w http.ResponseWriter, r
9090
}
9191

9292
func (s *systemRouter) getDiskUsage(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
93+
if err := httputils.ParseForm(r); err != nil {
94+
return err
95+
}
96+
97+
var getContainers, getImages, getVolumes, getBuildCache bool
98+
if typeStrs, ok := r.Form["type"]; !ok {
99+
getContainers, getImages, getVolumes, getBuildCache = true, true, true, true
100+
} else {
101+
for _, typ := range typeStrs {
102+
switch types.DiskUsageObject(typ) {
103+
case types.ContainerObject:
104+
getContainers = true
105+
case types.ImageObject:
106+
getImages = true
107+
case types.VolumeObject:
108+
getVolumes = true
109+
case types.BuildCacheObject:
110+
getBuildCache = true
111+
default:
112+
return invalidRequestError{Err: fmt.Errorf("unknown object type: %s", typ)}
113+
}
114+
}
115+
}
116+
93117
eg, ctx := errgroup.WithContext(ctx)
94118

95-
var du *types.DiskUsage
96-
eg.Go(func() error {
97-
var err error
98-
du, err = s.backend.SystemDiskUsage(ctx)
99-
return err
100-
})
119+
var systemDiskUsage *types.DiskUsage
120+
if getContainers || getImages || getVolumes {
121+
eg.Go(func() error {
122+
var err error
123+
systemDiskUsage, err = s.backend.SystemDiskUsage(ctx, DiskUsageOptions{
124+
Containers: getContainers,
125+
Images: getImages,
126+
Volumes: getVolumes,
127+
})
128+
return err
129+
})
130+
}
101131

102132
var buildCache []*types.BuildCache
103-
eg.Go(func() error {
104-
var err error
105-
buildCache, err = s.builder.DiskUsage(ctx)
106-
if err != nil {
107-
return pkgerrors.Wrap(err, "error getting build cache usage")
108-
}
109-
return nil
110-
})
133+
if getBuildCache {
134+
eg.Go(func() error {
135+
var err error
136+
buildCache, err = s.builder.DiskUsage(ctx)
137+
if err != nil {
138+
return errors.Wrap(err, "error getting build cache usage")
139+
}
140+
if buildCache == nil {
141+
// Ensure empty `BuildCache` field is represented as empty JSON array(`[]`)
142+
// instead of `null` to be consistent with `Images`, `Containers` etc.
143+
buildCache = []*types.BuildCache{}
144+
}
145+
return nil
146+
})
147+
}
111148

112149
if err := eg.Wait(); err != nil {
113150
return err
114151
}
115152

153+
var builderSize int64
116154
if versions.LessThan(httputils.VersionFromContext(ctx), "1.42") {
117-
var builderSize int64
118155
for _, b := range buildCache {
119156
builderSize += b.Size
120157
}
121-
du.BuilderSize = builderSize
122158
}
123159

124-
du.BuildCache = buildCache
125-
if buildCache == nil {
126-
// Ensure empty `BuildCache` field is represented as empty JSON array(`[]`)
127-
// instead of `null` to be consistent with `Images`, `Containers` etc.
128-
du.BuildCache = []*types.BuildCache{}
160+
du := types.DiskUsage{
161+
BuildCache: buildCache,
162+
BuilderSize: builderSize,
163+
}
164+
if systemDiskUsage != nil {
165+
du.LayersSize = systemDiskUsage.LayersSize
166+
du.Images = systemDiskUsage.Images
167+
du.Containers = systemDiskUsage.Containers
168+
du.Volumes = systemDiskUsage.Volumes
129169
}
130-
131170
return httputils.WriteJSON(w, http.StatusOK, du)
132171
}
133172

api/swagger.yaml

+10
Original file line numberDiff line numberDiff line change
@@ -8371,6 +8371,16 @@ paths:
83718371
description: "server error"
83728372
schema:
83738373
$ref: "#/definitions/ErrorResponse"
8374+
parameters:
8375+
- name: "type"
8376+
in: "query"
8377+
description: |
8378+
Object types, for which to compute and return data.
8379+
type: "array"
8380+
collectionFormat: multi
8381+
items:
8382+
type: "string"
8383+
enum: ["container", "image", "volume", "build-cache"]
83748384
tags: ["System"]
83758385
/images/{name}/get:
83768386
get:

api/types/types.go

+21
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,27 @@ type ShimConfig struct {
535535
Opts interface{}
536536
}
537537

538+
// DiskUsageObject represents an object type used for disk usage query filtering.
539+
type DiskUsageObject string
540+
541+
const (
542+
// ContainerObject represents a container DiskUsageObject.
543+
ContainerObject DiskUsageObject = "container"
544+
// ImageObject represents an image DiskUsageObject.
545+
ImageObject DiskUsageObject = "image"
546+
// VolumeObject represents a volume DiskUsageObject.
547+
VolumeObject DiskUsageObject = "volume"
548+
// BuildCacheObject represents a build-cache DiskUsageObject.
549+
BuildCacheObject DiskUsageObject = "build-cache"
550+
)
551+
552+
// DiskUsageOptions holds parameters for system disk usage query.
553+
type DiskUsageOptions struct {
554+
// Types specifies what object types to include in the response. If empty,
555+
// all object types are returned.
556+
Types []DiskUsageObject
557+
}
558+
538559
// DiskUsage contains response of Engine API:
539560
// GET "/system/df"
540561
type DiskUsage struct {

client/disk_usage.go

+13-6
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,30 @@ import (
44
"context"
55
"encoding/json"
66
"fmt"
7+
"net/url"
78

89
"github.com/docker/docker/api/types"
910
)
1011

1112
// DiskUsage requests the current data usage from the daemon
12-
func (cli *Client) DiskUsage(ctx context.Context) (types.DiskUsage, error) {
13-
var du types.DiskUsage
13+
func (cli *Client) DiskUsage(ctx context.Context, options types.DiskUsageOptions) (types.DiskUsage, error) {
14+
var query url.Values
15+
if len(options.Types) > 0 {
16+
query = url.Values{}
17+
for _, t := range options.Types {
18+
query.Add("type", string(t))
19+
}
20+
}
1421

15-
serverResp, err := cli.get(ctx, "/system/df", nil, nil)
22+
serverResp, err := cli.get(ctx, "/system/df", query, nil)
1623
defer ensureReaderClosed(serverResp)
1724
if err != nil {
18-
return du, err
25+
return types.DiskUsage{}, err
1926
}
2027

28+
var du types.DiskUsage
2129
if err := json.NewDecoder(serverResp.body).Decode(&du); err != nil {
22-
return du, fmt.Errorf("Error retrieving disk usage: %v", err)
30+
return types.DiskUsage{}, fmt.Errorf("Error retrieving disk usage: %v", err)
2331
}
24-
2532
return du, nil
2633
}

client/disk_usage_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ func TestDiskUsageError(t *testing.T) {
1818
client := &Client{
1919
client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
2020
}
21-
_, err := client.DiskUsage(context.Background())
21+
_, err := client.DiskUsage(context.Background(), types.DiskUsageOptions{})
2222
if !errdefs.IsSystem(err) {
2323
t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err)
2424
}
@@ -50,7 +50,7 @@ func TestDiskUsage(t *testing.T) {
5050
}, nil
5151
}),
5252
}
53-
if _, err := client.DiskUsage(context.Background()); err != nil {
53+
if _, err := client.DiskUsage(context.Background(), types.DiskUsageOptions{}); err != nil {
5454
t.Fatal(err)
5555
}
5656
}

client/interface.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ type SystemAPIClient interface {
168168
Events(ctx context.Context, options types.EventsOptions) (<-chan events.Message, <-chan error)
169169
Info(ctx context.Context) (types.Info, error)
170170
RegistryLogin(ctx context.Context, auth types.AuthConfig) (registry.AuthenticateOKBody, error)
171-
DiskUsage(ctx context.Context) (types.DiskUsage, error)
171+
DiskUsage(ctx context.Context, options types.DiskUsageOptions) (types.DiskUsage, error)
172172
Ping(ctx context.Context) (types.Ping, error)
173173
}
174174

daemon/disk_usage.go

+42-28
Original file line numberDiff line numberDiff line change
@@ -5,50 +5,64 @@ import (
55
"fmt"
66
"sync/atomic"
77

8+
"github.com/docker/docker/api/server/router/system"
89
"github.com/docker/docker/api/types"
910
"github.com/docker/docker/api/types/filters"
1011
)
1112

1213
// SystemDiskUsage returns information about the daemon data disk usage
13-
func (daemon *Daemon) SystemDiskUsage(ctx context.Context) (*types.DiskUsage, error) {
14+
func (daemon *Daemon) SystemDiskUsage(ctx context.Context, opts system.DiskUsageOptions) (*types.DiskUsage, error) {
1415
if !atomic.CompareAndSwapInt32(&daemon.diskUsageRunning, 0, 1) {
1516
return nil, fmt.Errorf("a disk usage operation is already running")
1617
}
1718
defer atomic.StoreInt32(&daemon.diskUsageRunning, 0)
1819

19-
// Retrieve container list
20-
allContainers, err := daemon.Containers(&types.ContainerListOptions{
21-
Size: true,
22-
All: true,
23-
})
24-
if err != nil {
25-
return nil, fmt.Errorf("failed to retrieve container list: %v", err)
26-
}
20+
var err error
2721

28-
// Get all top images with extra attributes
29-
allImages, err := daemon.imageService.Images(ctx, types.ImageListOptions{
30-
Filters: filters.NewArgs(),
31-
SharedSize: true,
32-
ContainerCount: true,
33-
})
34-
if err != nil {
35-
return nil, fmt.Errorf("failed to retrieve image list: %v", err)
22+
var containers []*types.Container
23+
if opts.Containers {
24+
// Retrieve container list
25+
containers, err = daemon.Containers(&types.ContainerListOptions{
26+
Size: true,
27+
All: true,
28+
})
29+
if err != nil {
30+
return nil, fmt.Errorf("failed to retrieve container list: %v", err)
31+
}
3632
}
3733

38-
localVolumes, err := daemon.volumes.LocalVolumesSize(ctx)
39-
if err != nil {
40-
return nil, err
41-
}
34+
var (
35+
images []*types.ImageSummary
36+
layersSize int64
37+
)
38+
if opts.Images {
39+
// Get all top images with extra attributes
40+
images, err = daemon.imageService.Images(ctx, types.ImageListOptions{
41+
Filters: filters.NewArgs(),
42+
SharedSize: true,
43+
ContainerCount: true,
44+
})
45+
if err != nil {
46+
return nil, fmt.Errorf("failed to retrieve image list: %v", err)
47+
}
4248

43-
allLayersSize, err := daemon.imageService.LayerDiskUsage(ctx)
44-
if err != nil {
45-
return nil, err
49+
layersSize, err = daemon.imageService.LayerDiskUsage(ctx)
50+
if err != nil {
51+
return nil, err
52+
}
4653
}
4754

55+
var volumes []*types.Volume
56+
if opts.Volumes {
57+
volumes, err = daemon.volumes.LocalVolumesSize(ctx)
58+
if err != nil {
59+
return nil, err
60+
}
61+
}
4862
return &types.DiskUsage{
49-
LayersSize: allLayersSize,
50-
Containers: allContainers,
51-
Volumes: localVolumes,
52-
Images: allImages,
63+
LayersSize: layersSize,
64+
Containers: containers,
65+
Volumes: volumes,
66+
Images: images,
5367
}, nil
5468
}

docs/api/version-history.md

+4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ keywords: "API, Docker, rcli, REST, documentation"
2424
* `GET /images/json` now accepts query parameter `shared-size`. When set `true`,
2525
images returned will include `SharedSize`, which provides the size on disk shared
2626
with other images present on the system.
27+
* `GET /system/df` now accepts query parameter `type`. When set,
28+
computes and returns data only for the specified object type.
29+
The parameter can be specified multiple times to select several object types.
30+
Supported values are: `container`, `image`, `volume`, `build-cache`.
2731

2832
## v1.41 API changes
2933

integration/build/build_session_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,14 @@ func TestBuildWithSession(t *testing.T) {
5353
assert.Check(t, is.Equal(strings.Count(out, "Using cache"), 2))
5454
assert.Check(t, is.Contains(out, "contentcontent"))
5555

56-
du, err := client.DiskUsage(context.TODO())
56+
du, err := client.DiskUsage(context.TODO(), types.DiskUsageOptions{})
5757
assert.Check(t, err)
5858
assert.Check(t, du.BuilderSize > 10)
5959

6060
out = testBuildWithSession(t, client, client.DaemonHost(), fctx.Dir, dockerfile)
6161
assert.Check(t, is.Equal(strings.Count(out, "Using cache"), 4))
6262

63-
du2, err := client.DiskUsage(context.TODO())
63+
du2, err := client.DiskUsage(context.TODO(), types.DiskUsageOptions{})
6464
assert.Check(t, err)
6565
assert.Check(t, is.Equal(du.BuilderSize, du2.BuilderSize))
6666

@@ -84,7 +84,7 @@ func TestBuildWithSession(t *testing.T) {
8484
_, err = client.BuildCachePrune(context.TODO(), types.BuildCachePruneOptions{All: true})
8585
assert.Check(t, err)
8686

87-
du, err = client.DiskUsage(context.TODO())
87+
du, err = client.DiskUsage(context.TODO(), types.DiskUsageOptions{})
8888
assert.Check(t, err)
8989
assert.Check(t, is.Equal(du.BuilderSize, int64(0)))
9090
}

0 commit comments

Comments
 (0)