mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Add /system/df API endpoint
This endpoint return data regarding the space used by docker on disk Signed-off-by: Kenfe-Mickael Laventure <mickael.laventure@gmail.com>
This commit is contained in:
parent
b717de5153
commit
f2e11fb8d1
6 changed files with 148 additions and 0 deletions
|
@ -14,6 +14,7 @@ import (
|
||||||
type Backend interface {
|
type Backend interface {
|
||||||
SystemInfo() (*types.Info, error)
|
SystemInfo() (*types.Info, error)
|
||||||
SystemVersion() types.Version
|
SystemVersion() types.Version
|
||||||
|
SystemDiskUsage() (*types.DiskUsage, error)
|
||||||
SubscribeToEvents(since, until time.Time, ef filters.Args) ([]events.Message, chan interface{})
|
SubscribeToEvents(since, until time.Time, ef filters.Args) ([]events.Message, chan interface{})
|
||||||
UnsubscribeFromEvents(chan interface{})
|
UnsubscribeFromEvents(chan interface{})
|
||||||
AuthenticateToRegistry(ctx context.Context, authConfig *types.AuthConfig) (string, string, error)
|
AuthenticateToRegistry(ctx context.Context, authConfig *types.AuthConfig) (string, string, error)
|
||||||
|
|
|
@ -26,6 +26,7 @@ func NewRouter(b Backend, c *cluster.Cluster) router.Router {
|
||||||
router.Cancellable(router.NewGetRoute("/events", r.getEvents)),
|
router.Cancellable(router.NewGetRoute("/events", r.getEvents)),
|
||||||
router.NewGetRoute("/info", r.getInfo),
|
router.NewGetRoute("/info", r.getInfo),
|
||||||
router.NewGetRoute("/version", r.getVersion),
|
router.NewGetRoute("/version", r.getVersion),
|
||||||
|
router.NewGetRoute("/system/df", r.getDiskUsage),
|
||||||
router.NewPostRoute("/auth", r.postAuth),
|
router.NewPostRoute("/auth", r.postAuth),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,15 @@ func (s *systemRouter) getVersion(ctx context.Context, w http.ResponseWriter, r
|
||||||
return httputils.WriteJSON(w, http.StatusOK, info)
|
return httputils.WriteJSON(w, http.StatusOK, info)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *systemRouter) getDiskUsage(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
du, err := s.backend.SystemDiskUsage()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return httputils.WriteJSON(w, http.StatusOK, du)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *systemRouter) getEvents(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (s *systemRouter) getEvents(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
if err := httputils.ParseForm(r); err != nil {
|
if err := httputils.ParseForm(r); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -530,3 +530,12 @@ type Runtime struct {
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
Args []string `json:"runtimeArgs,omitempty"`
|
Args []string `json:"runtimeArgs,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DiskUsage contains response of Remote API:
|
||||||
|
// GET "/system/df"
|
||||||
|
type DiskUsage struct {
|
||||||
|
LayersSize int64
|
||||||
|
Images []*Image
|
||||||
|
Containers []*Container
|
||||||
|
Volumes []*Volume
|
||||||
|
}
|
||||||
|
|
100
daemon/disk_usage.go
Normal file
100
daemon/disk_usage.go
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/distribution/digest"
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/layer"
|
||||||
|
"github.com/docker/docker/pkg/directory"
|
||||||
|
"github.com/docker/docker/volume"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (daemon *Daemon) getLayerRefs() map[layer.ChainID]int {
|
||||||
|
tmpImages := daemon.imageStore.Map()
|
||||||
|
layerRefs := map[layer.ChainID]int{}
|
||||||
|
for id, img := range tmpImages {
|
||||||
|
dgst := digest.Digest(id)
|
||||||
|
if len(daemon.referenceStore.References(dgst)) == 0 && len(daemon.imageStore.Children(id)) != 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
rootFS := *img.RootFS
|
||||||
|
rootFS.DiffIDs = nil
|
||||||
|
for _, id := range img.RootFS.DiffIDs {
|
||||||
|
rootFS.Append(id)
|
||||||
|
chid := rootFS.ChainID()
|
||||||
|
layerRefs[chid]++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return layerRefs
|
||||||
|
}
|
||||||
|
|
||||||
|
// SystemDiskUsage returns information about the daemon data disk usage
|
||||||
|
func (daemon *Daemon) SystemDiskUsage() (*types.DiskUsage, error) {
|
||||||
|
// Retrieve container list
|
||||||
|
allContainers, err := daemon.Containers(&types.ContainerListOptions{
|
||||||
|
Size: true,
|
||||||
|
All: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to retrieve container list: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all top images with extra attributes
|
||||||
|
allImages, err := daemon.Images("", "", false, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to retrieve image list: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all local volumes
|
||||||
|
allVolumes := []*types.Volume{}
|
||||||
|
getLocalVols := func(v volume.Volume) error {
|
||||||
|
name := v.Name()
|
||||||
|
refs := daemon.volumes.Refs(v)
|
||||||
|
|
||||||
|
tv := volumeToAPIType(v)
|
||||||
|
tv.RefCount = len(refs)
|
||||||
|
sz, err := directory.Size(v.Path())
|
||||||
|
if err != nil {
|
||||||
|
logrus.Warnf("failed to determine size of volume %v", name)
|
||||||
|
sz = -1
|
||||||
|
}
|
||||||
|
tv.Size = sz
|
||||||
|
allVolumes = append(allVolumes, tv)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = daemon.traverseLocalVolumes(getLocalVols)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get total layers size on disk
|
||||||
|
layerRefs := daemon.getLayerRefs()
|
||||||
|
allLayers := daemon.layerStore.Map()
|
||||||
|
var allLayersSize int64
|
||||||
|
for _, l := range allLayers {
|
||||||
|
size, err := l.DiffSize()
|
||||||
|
if err == nil {
|
||||||
|
if _, ok := layerRefs[l.ChainID()]; ok {
|
||||||
|
allLayersSize += size
|
||||||
|
} else {
|
||||||
|
logrus.Warnf("found leaked image layer %v", l.ChainID())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logrus.Warnf("failed to get diff size for layer %v", l.ChainID())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return &types.DiskUsage{
|
||||||
|
LayersSize: allLayersSize,
|
||||||
|
Containers: allContainers,
|
||||||
|
Volumes: allVolumes,
|
||||||
|
Images: allImages,
|
||||||
|
}, nil
|
||||||
|
}
|
|
@ -7,12 +7,14 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
dockererrors "github.com/docker/docker/api/errors"
|
dockererrors "github.com/docker/docker/api/errors"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
containertypes "github.com/docker/docker/api/types/container"
|
containertypes "github.com/docker/docker/api/types/container"
|
||||||
mounttypes "github.com/docker/docker/api/types/mount"
|
mounttypes "github.com/docker/docker/api/types/mount"
|
||||||
"github.com/docker/docker/container"
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/volume"
|
"github.com/docker/docker/volume"
|
||||||
|
"github.com/docker/docker/volume/drivers"
|
||||||
"github.com/opencontainers/runc/libcontainer/label"
|
"github.com/opencontainers/runc/libcontainer/label"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -276,3 +278,29 @@ func backportMountSpec(container *container.Container) error {
|
||||||
}
|
}
|
||||||
return container.ToDiskLocking()
|
return container.ToDiskLocking()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (daemon *Daemon) traverseLocalVolumes(fn func(volume.Volume) error) error {
|
||||||
|
localVolumeDriver, err := volumedrivers.GetDriver(volume.DefaultDriverName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't retrieve local volume driver: %v", err)
|
||||||
|
}
|
||||||
|
vols, err := localVolumeDriver.List()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't retrieve local volumes: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range vols {
|
||||||
|
name := v.Name()
|
||||||
|
_, err := daemon.volumes.Get(name)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Warnf("failed to retrieve volume %s from store: %v", name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = fn(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue