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  }