github.com/rhatdan/docker@v0.7.7-0.20180119204836-47a0dcbcd20a/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() map[layer.ChainID]int { 19 tmpImages := daemon.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.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 allImages, err := daemon.Images(filters.NewArgs(), false, true) 57 if err != nil { 58 return nil, fmt.Errorf("failed to retrieve image list: %v", err) 59 } 60 61 // Get all local volumes 62 allVolumes := []*types.Volume{} 63 getLocalVols := func(v volume.Volume) error { 64 select { 65 case <-ctx.Done(): 66 return ctx.Err() 67 default: 68 if d, ok := v.(volume.DetailedVolume); ok { 69 // skip local volumes with mount options since these could have external 70 // mounted filesystems that will be slow to enumerate. 71 if len(d.Options()) > 0 { 72 return nil 73 } 74 } 75 name := v.Name() 76 refs := daemon.volumes.Refs(v) 77 78 tv := volumeToAPIType(v) 79 sz, err := directory.Size(v.Path()) 80 if err != nil { 81 logrus.Warnf("failed to determine size of volume %v", name) 82 sz = -1 83 } 84 tv.UsageData = &types.VolumeUsageData{Size: sz, RefCount: int64(len(refs))} 85 allVolumes = append(allVolumes, tv) 86 } 87 88 return nil 89 } 90 91 err = daemon.traverseLocalVolumes(getLocalVols) 92 if err != nil { 93 return nil, err 94 } 95 96 // Get total layers size on disk 97 var allLayersSize int64 98 layerRefs := daemon.getLayerRefs() 99 for _, ls := range daemon.layerStores { 100 allLayers := ls.Map() 101 for _, l := range allLayers { 102 select { 103 case <-ctx.Done(): 104 return nil, ctx.Err() 105 default: 106 size, err := l.DiffSize() 107 if err == nil { 108 if _, ok := layerRefs[l.ChainID()]; ok { 109 allLayersSize += size 110 } else { 111 logrus.Warnf("found leaked image layer %v", l.ChainID()) 112 } 113 } else { 114 logrus.Warnf("failed to get diff size for layer %v", l.ChainID()) 115 } 116 } 117 } 118 } 119 120 return &types.DiskUsage{ 121 LayersSize: allLayersSize, 122 Containers: allContainers, 123 Volumes: allVolumes, 124 Images: allImages, 125 }, nil 126 }