github.com/sld880311/docker@v0.0.0-20200524143708-d5593973a475/cli/command/formatter/disk_usage.go (about) 1 package formatter 2 3 import ( 4 "bytes" 5 "fmt" 6 "strings" 7 "text/template" 8 9 "github.com/docker/distribution/reference" 10 "github.com/docker/docker/api/types" 11 units "github.com/docker/go-units" 12 ) 13 14 const ( 15 defaultDiskUsageImageTableFormat = "table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.CreatedSince}} ago\t{{.VirtualSize}}\t{{.SharedSize}}\t{{.UniqueSize}}\t{{.Containers}}" 16 defaultDiskUsageContainerTableFormat = "table {{.ID}}\t{{.Image}}\t{{.Command}}\t{{.LocalVolumes}}\t{{.Size}}\t{{.RunningFor}} ago\t{{.Status}}\t{{.Names}}" 17 defaultDiskUsageVolumeTableFormat = "table {{.Name}}\t{{.Links}}\t{{.Size}}" 18 defaultDiskUsageTableFormat = "table {{.Type}}\t{{.TotalCount}}\t{{.Active}}\t{{.Size}}\t{{.Reclaimable}}" 19 20 typeHeader = "TYPE" 21 totalHeader = "TOTAL" 22 activeHeader = "ACTIVE" 23 reclaimableHeader = "RECLAIMABLE" 24 containersHeader = "CONTAINERS" 25 sharedSizeHeader = "SHARED SIZE" 26 uniqueSizeHeader = "UNIQUE SiZE" 27 ) 28 29 // DiskUsageContext contains disk usage specific information required by the formater, encapsulate a Context struct. 30 type DiskUsageContext struct { 31 Context 32 Verbose bool 33 LayersSize int64 34 Images []*types.ImageSummary 35 Containers []*types.Container 36 Volumes []*types.Volume 37 } 38 39 func (ctx *DiskUsageContext) startSubsection(format string) (*template.Template, error) { 40 ctx.buffer = bytes.NewBufferString("") 41 ctx.header = "" 42 ctx.Format = Format(format) 43 ctx.preFormat() 44 45 return ctx.parseFormat() 46 } 47 48 func (ctx *DiskUsageContext) Write() { 49 if ctx.Verbose == false { 50 ctx.buffer = bytes.NewBufferString("") 51 ctx.Format = defaultDiskUsageTableFormat 52 ctx.preFormat() 53 54 tmpl, err := ctx.parseFormat() 55 if err != nil { 56 return 57 } 58 59 err = ctx.contextFormat(tmpl, &diskUsageImagesContext{ 60 totalSize: ctx.LayersSize, 61 images: ctx.Images, 62 }) 63 if err != nil { 64 return 65 } 66 err = ctx.contextFormat(tmpl, &diskUsageContainersContext{ 67 containers: ctx.Containers, 68 }) 69 if err != nil { 70 return 71 } 72 73 err = ctx.contextFormat(tmpl, &diskUsageVolumesContext{ 74 volumes: ctx.Volumes, 75 }) 76 if err != nil { 77 return 78 } 79 80 ctx.postFormat(tmpl, &diskUsageContainersContext{containers: []*types.Container{}}) 81 82 return 83 } 84 85 // First images 86 tmpl, err := ctx.startSubsection(defaultDiskUsageImageTableFormat) 87 if err != nil { 88 return 89 } 90 91 ctx.Output.Write([]byte("Images space usage:\n\n")) 92 for _, i := range ctx.Images { 93 repo := "<none>" 94 tag := "<none>" 95 if len(i.RepoTags) > 0 && !isDangling(*i) { 96 // Only show the first tag 97 ref, err := reference.ParseNamed(i.RepoTags[0]) 98 if err != nil { 99 continue 100 } 101 if nt, ok := ref.(reference.NamedTagged); ok { 102 repo = ref.Name() 103 tag = nt.Tag() 104 } 105 } 106 107 err = ctx.contextFormat(tmpl, &imageContext{ 108 repo: repo, 109 tag: tag, 110 trunc: true, 111 i: *i, 112 }) 113 if err != nil { 114 return 115 } 116 } 117 ctx.postFormat(tmpl, &imageContext{}) 118 119 // Now containers 120 ctx.Output.Write([]byte("\nContainers space usage:\n\n")) 121 tmpl, err = ctx.startSubsection(defaultDiskUsageContainerTableFormat) 122 if err != nil { 123 return 124 } 125 for _, c := range ctx.Containers { 126 // Don't display the virtual size 127 c.SizeRootFs = 0 128 err = ctx.contextFormat(tmpl, &containerContext{ 129 trunc: true, 130 c: *c, 131 }) 132 if err != nil { 133 return 134 } 135 } 136 ctx.postFormat(tmpl, &containerContext{}) 137 138 // And volumes 139 ctx.Output.Write([]byte("\nLocal Volumes space usage:\n\n")) 140 tmpl, err = ctx.startSubsection(defaultDiskUsageVolumeTableFormat) 141 if err != nil { 142 return 143 } 144 for _, v := range ctx.Volumes { 145 err = ctx.contextFormat(tmpl, &volumeContext{ 146 v: *v, 147 }) 148 if err != nil { 149 return 150 } 151 } 152 ctx.postFormat(tmpl, &volumeContext{v: types.Volume{}}) 153 } 154 155 type diskUsageImagesContext struct { 156 HeaderContext 157 totalSize int64 158 images []*types.ImageSummary 159 } 160 161 func (c *diskUsageImagesContext) Type() string { 162 c.AddHeader(typeHeader) 163 return "Images" 164 } 165 166 func (c *diskUsageImagesContext) TotalCount() string { 167 c.AddHeader(totalHeader) 168 return fmt.Sprintf("%d", len(c.images)) 169 } 170 171 func (c *diskUsageImagesContext) Active() string { 172 c.AddHeader(activeHeader) 173 used := 0 174 for _, i := range c.images { 175 if i.Containers > 0 { 176 used++ 177 } 178 } 179 180 return fmt.Sprintf("%d", used) 181 } 182 183 func (c *diskUsageImagesContext) Size() string { 184 c.AddHeader(sizeHeader) 185 return units.HumanSize(float64(c.totalSize)) 186 187 } 188 189 func (c *diskUsageImagesContext) Reclaimable() string { 190 var used int64 191 192 c.AddHeader(reclaimableHeader) 193 for _, i := range c.images { 194 if i.Containers != 0 { 195 if i.VirtualSize == -1 || i.SharedSize == -1 { 196 continue 197 } 198 used += i.VirtualSize - i.SharedSize 199 } 200 } 201 202 reclaimable := c.totalSize - used 203 if c.totalSize > 0 { 204 return fmt.Sprintf("%s (%v%%)", units.HumanSize(float64(reclaimable)), (reclaimable*100)/c.totalSize) 205 } 206 return fmt.Sprintf("%s", units.HumanSize(float64(reclaimable))) 207 } 208 209 type diskUsageContainersContext struct { 210 HeaderContext 211 verbose bool 212 containers []*types.Container 213 } 214 215 func (c *diskUsageContainersContext) Type() string { 216 c.AddHeader(typeHeader) 217 return "Containers" 218 } 219 220 func (c *diskUsageContainersContext) TotalCount() string { 221 c.AddHeader(totalHeader) 222 return fmt.Sprintf("%d", len(c.containers)) 223 } 224 225 func (c *diskUsageContainersContext) isActive(container types.Container) bool { 226 return strings.Contains(container.State, "running") || 227 strings.Contains(container.State, "paused") || 228 strings.Contains(container.State, "restarting") 229 } 230 231 func (c *diskUsageContainersContext) Active() string { 232 c.AddHeader(activeHeader) 233 used := 0 234 for _, container := range c.containers { 235 if c.isActive(*container) { 236 used++ 237 } 238 } 239 240 return fmt.Sprintf("%d", used) 241 } 242 243 func (c *diskUsageContainersContext) Size() string { 244 var size int64 245 246 c.AddHeader(sizeHeader) 247 for _, container := range c.containers { 248 size += container.SizeRw 249 } 250 251 return units.HumanSize(float64(size)) 252 } 253 254 func (c *diskUsageContainersContext) Reclaimable() string { 255 var reclaimable int64 256 var totalSize int64 257 258 c.AddHeader(reclaimableHeader) 259 for _, container := range c.containers { 260 if !c.isActive(*container) { 261 reclaimable += container.SizeRw 262 } 263 totalSize += container.SizeRw 264 } 265 266 if totalSize > 0 { 267 return fmt.Sprintf("%s (%v%%)", units.HumanSize(float64(reclaimable)), (reclaimable*100)/totalSize) 268 } 269 270 return fmt.Sprintf("%s", units.HumanSize(float64(reclaimable))) 271 } 272 273 type diskUsageVolumesContext struct { 274 HeaderContext 275 verbose bool 276 volumes []*types.Volume 277 } 278 279 func (c *diskUsageVolumesContext) Type() string { 280 c.AddHeader(typeHeader) 281 return "Local Volumes" 282 } 283 284 func (c *diskUsageVolumesContext) TotalCount() string { 285 c.AddHeader(totalHeader) 286 return fmt.Sprintf("%d", len(c.volumes)) 287 } 288 289 func (c *diskUsageVolumesContext) Active() string { 290 c.AddHeader(activeHeader) 291 292 used := 0 293 for _, v := range c.volumes { 294 if v.UsageData.RefCount > 0 { 295 used++ 296 } 297 } 298 299 return fmt.Sprintf("%d", used) 300 } 301 302 func (c *diskUsageVolumesContext) Size() string { 303 var size int64 304 305 c.AddHeader(sizeHeader) 306 for _, v := range c.volumes { 307 if v.UsageData.Size != -1 { 308 size += v.UsageData.Size 309 } 310 } 311 312 return units.HumanSize(float64(size)) 313 } 314 315 func (c *diskUsageVolumesContext) Reclaimable() string { 316 var reclaimable int64 317 var totalSize int64 318 319 c.AddHeader(reclaimableHeader) 320 for _, v := range c.volumes { 321 if v.UsageData.Size != -1 { 322 if v.UsageData.RefCount == 0 { 323 reclaimable += v.UsageData.Size 324 } 325 totalSize += v.UsageData.Size 326 } 327 } 328 329 if totalSize > 0 { 330 return fmt.Sprintf("%s (%v%%)", units.HumanSize(float64(reclaimable)), (reclaimable*100)/totalSize) 331 } 332 333 return fmt.Sprintf("%s", units.HumanSize(float64(reclaimable))) 334 }