123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 |
- package formatter
- import (
- "bytes"
- "fmt"
- "strings"
- "text/template"
- "github.com/docker/distribution/reference"
- "github.com/docker/docker/api/types"
- units "github.com/docker/go-units"
- )
- const (
- defaultDiskUsageImageTableFormat = "table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.CreatedSince}} ago\t{{.VirtualSize}}\t{{.SharedSize}}\t{{.UniqueSize}}\t{{.Containers}}"
- defaultDiskUsageContainerTableFormat = "table {{.ID}}\t{{.Image}}\t{{.Command}}\t{{.LocalVolumes}}\t{{.Size}}\t{{.RunningFor}} ago\t{{.Status}}\t{{.Names}}"
- defaultDiskUsageVolumeTableFormat = "table {{.Name}}\t{{.Links}}\t{{.Size}}"
- defaultDiskUsageTableFormat = "table {{.Type}}\t{{.TotalCount}}\t{{.Active}}\t{{.Size}}\t{{.Reclaimable}}"
- typeHeader = "TYPE"
- totalHeader = "TOTAL"
- activeHeader = "ACTIVE"
- reclaimableHeader = "RECLAIMABLE"
- containersHeader = "CONTAINERS"
- sharedSizeHeader = "SHARED SIZE"
- uniqueSizeHeader = "UNIQUE SiZE"
- )
- // DiskUsageContext contains disk usage specific information required by the formater, encapsulate a Context struct.
- type DiskUsageContext struct {
- Context
- Verbose bool
- LayersSize int64
- Images []*types.ImageSummary
- Containers []*types.Container
- Volumes []*types.Volume
- }
- func (ctx *DiskUsageContext) startSubsection(format string) (*template.Template, error) {
- ctx.buffer = bytes.NewBufferString("")
- ctx.header = ""
- ctx.Format = Format(format)
- ctx.preFormat()
- return ctx.parseFormat()
- }
- func (ctx *DiskUsageContext) Write() {
- if ctx.Verbose == false {
- ctx.buffer = bytes.NewBufferString("")
- ctx.Format = defaultDiskUsageTableFormat
- ctx.preFormat()
- tmpl, err := ctx.parseFormat()
- if err != nil {
- return
- }
- err = ctx.contextFormat(tmpl, &diskUsageImagesContext{
- totalSize: ctx.LayersSize,
- images: ctx.Images,
- })
- if err != nil {
- return
- }
- err = ctx.contextFormat(tmpl, &diskUsageContainersContext{
- containers: ctx.Containers,
- })
- if err != nil {
- return
- }
- err = ctx.contextFormat(tmpl, &diskUsageVolumesContext{
- volumes: ctx.Volumes,
- })
- if err != nil {
- return
- }
- ctx.postFormat(tmpl, &diskUsageContainersContext{containers: []*types.Container{}})
- return
- }
- // First images
- tmpl, err := ctx.startSubsection(defaultDiskUsageImageTableFormat)
- if err != nil {
- return
- }
- ctx.Output.Write([]byte("Images space usage:\n\n"))
- for _, i := range ctx.Images {
- repo := "<none>"
- tag := "<none>"
- if len(i.RepoTags) > 0 && !isDangling(*i) {
- // Only show the first tag
- ref, err := reference.ParseNamed(i.RepoTags[0])
- if err != nil {
- continue
- }
- if nt, ok := ref.(reference.NamedTagged); ok {
- repo = ref.Name()
- tag = nt.Tag()
- }
- }
- err = ctx.contextFormat(tmpl, &imageContext{
- repo: repo,
- tag: tag,
- trunc: true,
- i: *i,
- })
- if err != nil {
- return
- }
- }
- ctx.postFormat(tmpl, &imageContext{})
- // Now containers
- ctx.Output.Write([]byte("\nContainers space usage:\n\n"))
- tmpl, err = ctx.startSubsection(defaultDiskUsageContainerTableFormat)
- if err != nil {
- return
- }
- for _, c := range ctx.Containers {
- // Don't display the virtual size
- c.SizeRootFs = 0
- err = ctx.contextFormat(tmpl, &containerContext{
- trunc: true,
- c: *c,
- })
- if err != nil {
- return
- }
- }
- ctx.postFormat(tmpl, &containerContext{})
- // And volumes
- ctx.Output.Write([]byte("\nLocal Volumes space usage:\n\n"))
- tmpl, err = ctx.startSubsection(defaultDiskUsageVolumeTableFormat)
- if err != nil {
- return
- }
- for _, v := range ctx.Volumes {
- err = ctx.contextFormat(tmpl, &volumeContext{
- v: *v,
- })
- if err != nil {
- return
- }
- }
- ctx.postFormat(tmpl, &volumeContext{v: types.Volume{}})
- }
- type diskUsageImagesContext struct {
- HeaderContext
- totalSize int64
- images []*types.ImageSummary
- }
- func (c *diskUsageImagesContext) Type() string {
- c.AddHeader(typeHeader)
- return "Images"
- }
- func (c *diskUsageImagesContext) TotalCount() string {
- c.AddHeader(totalHeader)
- return fmt.Sprintf("%d", len(c.images))
- }
- func (c *diskUsageImagesContext) Active() string {
- c.AddHeader(activeHeader)
- used := 0
- for _, i := range c.images {
- if i.Containers > 0 {
- used++
- }
- }
- return fmt.Sprintf("%d", used)
- }
- func (c *diskUsageImagesContext) Size() string {
- c.AddHeader(sizeHeader)
- return units.HumanSize(float64(c.totalSize))
- }
- func (c *diskUsageImagesContext) Reclaimable() string {
- var used int64
- c.AddHeader(reclaimableHeader)
- for _, i := range c.images {
- if i.Containers != 0 {
- used += i.Size
- }
- }
- reclaimable := c.totalSize - used
- if c.totalSize > 0 {
- return fmt.Sprintf("%s (%v%%)", units.HumanSize(float64(reclaimable)), (reclaimable*100)/c.totalSize)
- }
- return fmt.Sprintf("%s", units.HumanSize(float64(reclaimable)))
- }
- type diskUsageContainersContext struct {
- HeaderContext
- verbose bool
- containers []*types.Container
- }
- func (c *diskUsageContainersContext) Type() string {
- c.AddHeader(typeHeader)
- return "Containers"
- }
- func (c *diskUsageContainersContext) TotalCount() string {
- c.AddHeader(totalHeader)
- return fmt.Sprintf("%d", len(c.containers))
- }
- func (c *diskUsageContainersContext) isActive(container types.Container) bool {
- return strings.Contains(container.State, "running") ||
- strings.Contains(container.State, "paused") ||
- strings.Contains(container.State, "restarting")
- }
- func (c *diskUsageContainersContext) Active() string {
- c.AddHeader(activeHeader)
- used := 0
- for _, container := range c.containers {
- if c.isActive(*container) {
- used++
- }
- }
- return fmt.Sprintf("%d", used)
- }
- func (c *diskUsageContainersContext) Size() string {
- var size int64
- c.AddHeader(sizeHeader)
- for _, container := range c.containers {
- size += container.SizeRw
- }
- return units.HumanSize(float64(size))
- }
- func (c *diskUsageContainersContext) Reclaimable() string {
- var reclaimable int64
- var totalSize int64
- c.AddHeader(reclaimableHeader)
- for _, container := range c.containers {
- if !c.isActive(*container) {
- reclaimable += container.SizeRw
- }
- totalSize += container.SizeRw
- }
- if totalSize > 0 {
- return fmt.Sprintf("%s (%v%%)", units.HumanSize(float64(reclaimable)), (reclaimable*100)/totalSize)
- }
- return fmt.Sprintf("%s", units.HumanSize(float64(reclaimable)))
- }
- type diskUsageVolumesContext struct {
- HeaderContext
- verbose bool
- volumes []*types.Volume
- }
- func (c *diskUsageVolumesContext) Type() string {
- c.AddHeader(typeHeader)
- return "Local Volumes"
- }
- func (c *diskUsageVolumesContext) TotalCount() string {
- c.AddHeader(totalHeader)
- return fmt.Sprintf("%d", len(c.volumes))
- }
- func (c *diskUsageVolumesContext) Active() string {
- c.AddHeader(activeHeader)
- used := 0
- for _, v := range c.volumes {
- if v.UsageData.RefCount > 0 {
- used++
- }
- }
- return fmt.Sprintf("%d", used)
- }
- func (c *diskUsageVolumesContext) Size() string {
- var size int64
- c.AddHeader(sizeHeader)
- for _, v := range c.volumes {
- if v.UsageData.Size != -1 {
- size += v.UsageData.Size
- }
- }
- return units.HumanSize(float64(size))
- }
- func (c *diskUsageVolumesContext) Reclaimable() string {
- var reclaimable int64
- var totalSize int64
- c.AddHeader(reclaimableHeader)
- for _, v := range c.volumes {
- if v.UsageData.Size != -1 {
- if v.UsageData.RefCount == 0 {
- reclaimable += v.UsageData.Size
- }
- totalSize += v.UsageData.Size
- }
- }
- if totalSize > 0 {
- return fmt.Sprintf("%s (%v%%)", units.HumanSize(float64(reclaimable)), (reclaimable*100)/totalSize)
- }
- return fmt.Sprintf("%s", units.HumanSize(float64(reclaimable)))
- }
|