github.com/ssdev-go/moby@v17.12.1-ce-rc2+incompatible/daemon/disk_usage.go (about) 1 package daemon 2 3 import ( 4 "fmt" 5 "sync/atomic" 6 7 "golang.org/x/net/context" 8 9 "github.com/docker/docker/api/types" 10 "github.com/docker/docker/api/types/filters" 11 "github.com/docker/docker/layer" 12 "github.com/docker/docker/pkg/directory" 13 "github.com/docker/docker/volume" 14 "github.com/opencontainers/go-digest" 15 "github.com/sirupsen/logrus" 16 ) 17 18 func (daemon *Daemon) getLayerRefs(platform string) map[layer.ChainID]int { 19 tmpImages := daemon.stores[platform].imageStore.Map() 20 layerRefs := map[layer.ChainID]int{} 21 for id, img := range tmpImages { 22 dgst := digest.Digest(id) 23 if len(daemon.referenceStore.References(dgst)) == 0 && len(daemon.stores[platform].imageStore.Children(id)) != 0 { 24 continue 25 } 26 27 rootFS := *img.RootFS 28 rootFS.DiffIDs = nil 29 for _, id := range img.RootFS.DiffIDs { 30 rootFS.Append(id) 31 chid := rootFS.ChainID() 32 layerRefs[chid]++ 33 } 34 } 35 36 return layerRefs 37 } 38 39 // SystemDiskUsage returns information about the daemon data disk usage 40 func (daemon *Daemon) SystemDiskUsage(ctx context.Context) (*types.DiskUsage, error) { 41 if !atomic.CompareAndSwapInt32(&daemon.diskUsageRunning, 0, 1) { 42 return nil, fmt.Errorf("a disk usage operation is already running") 43 } 44 defer atomic.StoreInt32(&daemon.diskUsageRunning, 0) 45 46 // Retrieve container list 47 allContainers, err := daemon.Containers(&types.ContainerListOptions{ 48 Size: true, 49 All: true, 50 }) 51 if err != nil { 52 return nil, fmt.Errorf("failed to retrieve container list: %v", err) 53 } 54 55 // Get all top images with extra attributes 56 // TODO @jhowardmsft LCOW. This may need revisiting 57 allImages, err := daemon.Images(filters.NewArgs(), false, true) 58 if err != nil { 59 return nil, fmt.Errorf("failed to retrieve image list: %v", err) 60 } 61 62 // Get all local volumes 63 allVolumes := []*types.Volume{} 64 getLocalVols := func(v volume.Volume) error { 65 select { 66 case <-ctx.Done(): 67 return ctx.Err() 68 default: 69 if d, ok := v.(volume.DetailedVolume); ok { 70 // skip local volumes with mount options since these could have external 71 // mounted filesystems that will be slow to enumerate. 72 if len(d.Options()) > 0 { 73 return nil 74 } 75 } 76 name := v.Name() 77 refs := daemon.volumes.Refs(v) 78 79 tv := volumeToAPIType(v) 80 sz, err := directory.Size(v.Path()) 81 if err != nil { 82 logrus.Warnf("failed to determine size of volume %v", name) 83 sz = -1 84 } 85 tv.UsageData = &types.VolumeUsageData{Size: sz, RefCount: int64(len(refs))} 86 allVolumes = append(allVolumes, tv) 87 } 88 89 return nil 90 } 91 92 err = daemon.traverseLocalVolumes(getLocalVols) 93 if err != nil { 94 return nil, err 95 } 96 97 // Get total layers size on disk 98 var allLayersSize int64 99 for platform := range daemon.stores { 100 layerRefs := daemon.getLayerRefs(platform) 101 allLayers := daemon.stores[platform].layerStore.Map() 102 for _, l := range allLayers { 103 select { 104 case <-ctx.Done(): 105 return nil, ctx.Err() 106 default: 107 size, err := l.DiffSize() 108 if err == nil { 109 if _, ok := layerRefs[l.ChainID()]; ok { 110 allLayersSize += size 111 } else { 112 logrus.Warnf("found leaked image layer %v platform %s", l.ChainID(), platform) 113 } 114 } else { 115 logrus.Warnf("failed to get diff size for layer %v %s", l.ChainID(), platform) 116 } 117 } 118 } 119 } 120 121 return &types.DiskUsage{ 122 LayersSize: allLayersSize, 123 Containers: allContainers, 124 Volumes: allVolumes, 125 Images: allImages, 126 }, nil 127 }