github.com/fabiokung/docker@v0.11.2-0.20170222101415-4534dcd49497/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 formatter, 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.ParseNormalizedNamed(i.RepoTags[0]) 98 if err != nil { 99 continue 100 } 101 if nt, ok := ref.(reference.NamedTagged); ok { 102 repo = reference.FamiliarName(ref) 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) MarshalJSON() ([]byte, error) { 162 return marshalJSON(c) 163 } 164 165 func (c *diskUsageImagesContext) Type() string { 166 c.AddHeader(typeHeader) 167 return "Images" 168 } 169 170 func (c *diskUsageImagesContext) TotalCount() string { 171 c.AddHeader(totalHeader) 172 return fmt.Sprintf("%d", len(c.images)) 173 } 174 175 func (c *diskUsageImagesContext) Active() string { 176 c.AddHeader(activeHeader) 177 used := 0 178 for _, i := range c.images { 179 if i.Containers > 0 { 180 used++ 181 } 182 } 183 184 return fmt.Sprintf("%d", used) 185 } 186 187 func (c *diskUsageImagesContext) Size() string { 188 c.AddHeader(sizeHeader) 189 return units.HumanSize(float64(c.totalSize)) 190 191 } 192 193 func (c *diskUsageImagesContext) Reclaimable() string { 194 var used int64 195 196 c.AddHeader(reclaimableHeader) 197 for _, i := range c.images { 198 if i.Containers != 0 { 199 if i.VirtualSize == -1 || i.SharedSize == -1 { 200 continue 201 } 202 used += i.VirtualSize - i.SharedSize 203 } 204 } 205 206 reclaimable := c.totalSize - used 207 if c.totalSize > 0 { 208 return fmt.Sprintf("%s (%v%%)", units.HumanSize(float64(reclaimable)), (reclaimable*100)/c.totalSize) 209 } 210 return fmt.Sprintf("%s", units.HumanSize(float64(reclaimable))) 211 } 212 213 type diskUsageContainersContext struct { 214 HeaderContext 215 verbose bool 216 containers []*types.Container 217 } 218 219 func (c *diskUsageContainersContext) MarshalJSON() ([]byte, error) { 220 return marshalJSON(c) 221 } 222 223 func (c *diskUsageContainersContext) Type() string { 224 c.AddHeader(typeHeader) 225 return "Containers" 226 } 227 228 func (c *diskUsageContainersContext) TotalCount() string { 229 c.AddHeader(totalHeader) 230 return fmt.Sprintf("%d", len(c.containers)) 231 } 232 233 func (c *diskUsageContainersContext) isActive(container types.Container) bool { 234 return strings.Contains(container.State, "running") || 235 strings.Contains(container.State, "paused") || 236 strings.Contains(container.State, "restarting") 237 } 238 239 func (c *diskUsageContainersContext) Active() string { 240 c.AddHeader(activeHeader) 241 used := 0 242 for _, container := range c.containers { 243 if c.isActive(*container) { 244 used++ 245 } 246 } 247 248 return fmt.Sprintf("%d", used) 249 } 250 251 func (c *diskUsageContainersContext) Size() string { 252 var size int64 253 254 c.AddHeader(sizeHeader) 255 for _, container := range c.containers { 256 size += container.SizeRw 257 } 258 259 return units.HumanSize(float64(size)) 260 } 261 262 func (c *diskUsageContainersContext) Reclaimable() string { 263 var reclaimable int64 264 var totalSize int64 265 266 c.AddHeader(reclaimableHeader) 267 for _, container := range c.containers { 268 if !c.isActive(*container) { 269 reclaimable += container.SizeRw 270 } 271 totalSize += container.SizeRw 272 } 273 274 if totalSize > 0 { 275 return fmt.Sprintf("%s (%v%%)", units.HumanSize(float64(reclaimable)), (reclaimable*100)/totalSize) 276 } 277 278 return fmt.Sprintf("%s", units.HumanSize(float64(reclaimable))) 279 } 280 281 type diskUsageVolumesContext struct { 282 HeaderContext 283 verbose bool 284 volumes []*types.Volume 285 } 286 287 func (c *diskUsageVolumesContext) MarshalJSON() ([]byte, error) { 288 return marshalJSON(c) 289 } 290 291 func (c *diskUsageVolumesContext) Type() string { 292 c.AddHeader(typeHeader) 293 return "Local Volumes" 294 } 295 296 func (c *diskUsageVolumesContext) TotalCount() string { 297 c.AddHeader(totalHeader) 298 return fmt.Sprintf("%d", len(c.volumes)) 299 } 300 301 func (c *diskUsageVolumesContext) Active() string { 302 c.AddHeader(activeHeader) 303 304 used := 0 305 for _, v := range c.volumes { 306 if v.UsageData.RefCount > 0 { 307 used++ 308 } 309 } 310 311 return fmt.Sprintf("%d", used) 312 } 313 314 func (c *diskUsageVolumesContext) Size() string { 315 var size int64 316 317 c.AddHeader(sizeHeader) 318 for _, v := range c.volumes { 319 if v.UsageData.Size != -1 { 320 size += v.UsageData.Size 321 } 322 } 323 324 return units.HumanSize(float64(size)) 325 } 326 327 func (c *diskUsageVolumesContext) Reclaimable() string { 328 var reclaimable int64 329 var totalSize int64 330 331 c.AddHeader(reclaimableHeader) 332 for _, v := range c.volumes { 333 if v.UsageData.Size != -1 { 334 if v.UsageData.RefCount == 0 { 335 reclaimable += v.UsageData.Size 336 } 337 totalSize += v.UsageData.Size 338 } 339 } 340 341 if totalSize > 0 { 342 return fmt.Sprintf("%s (%v%%)", units.HumanSize(float64(reclaimable)), (reclaimable*100)/totalSize) 343 } 344 345 return fmt.Sprintf("%s", units.HumanSize(float64(reclaimable))) 346 }