github.com/olljanat/moby@v1.13.1/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  }