mirror of https://github.com/docker/cli.git
system df: show table output for build cache
Signed-off-by: Tibor Vass <tibor@docker.com>
(cherry picked from commit ca608c2302
)
Signed-off-by: Tibor Vass <tibor@docker.com>
This commit is contained in:
parent
9d43f1ed48
commit
3c37d6a034
|
@ -0,0 +1,165 @@
|
||||||
|
package formatter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/pkg/stringid"
|
||||||
|
"github.com/docker/go-units"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultBuildCacheTableFormat = "table {{.ID}}\t{{.Type}}\t{{.Size}}\t{{.CreatedSince}}\t{{.LastUsedSince}}\t{{.UsageCount}}\t{{.Shared}}\t{{.Description}}"
|
||||||
|
|
||||||
|
cacheIDHeader = "CACHE ID"
|
||||||
|
parentHeader = "PARENT"
|
||||||
|
lastUsedSinceHeader = "LAST USED"
|
||||||
|
usageCountHeader = "USAGE"
|
||||||
|
inUseHeader = "IN USE"
|
||||||
|
sharedHeader = "SHARED"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewBuildCacheFormat returns a Format for rendering using a Context
|
||||||
|
func NewBuildCacheFormat(source string, quiet bool) Format {
|
||||||
|
switch source {
|
||||||
|
case TableFormatKey:
|
||||||
|
if quiet {
|
||||||
|
return defaultQuietFormat
|
||||||
|
}
|
||||||
|
return Format(defaultBuildCacheTableFormat)
|
||||||
|
case RawFormatKey:
|
||||||
|
if quiet {
|
||||||
|
return `build_cache_id: {{.ID}}`
|
||||||
|
}
|
||||||
|
format := `build_cache_id: {{.ID}}
|
||||||
|
parent_id: {{.Parent}}
|
||||||
|
type: {{.Type}}
|
||||||
|
description: {{.Description}}
|
||||||
|
created_at: {{.CreatedSince}}
|
||||||
|
last_used_at: {{.LastUsedSince}}
|
||||||
|
usage_count: {{.UsageCount}}
|
||||||
|
in_use: {{.InUse}}
|
||||||
|
shared: {{.Shared}}
|
||||||
|
`
|
||||||
|
return Format(format)
|
||||||
|
}
|
||||||
|
return Format(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildCacheSort(buildCache []*types.BuildCache) {
|
||||||
|
sort.Slice(buildCache, func(i, j int) bool {
|
||||||
|
lui, luj := buildCache[i].LastUsedAt, buildCache[j].LastUsedAt
|
||||||
|
switch {
|
||||||
|
case lui == nil && luj == nil:
|
||||||
|
return strings.Compare(buildCache[i].ID, buildCache[j].ID) < 0
|
||||||
|
case lui == nil:
|
||||||
|
return true
|
||||||
|
case luj == nil:
|
||||||
|
return false
|
||||||
|
case lui.Equal(*luj):
|
||||||
|
return strings.Compare(buildCache[i].ID, buildCache[j].ID) < 0
|
||||||
|
default:
|
||||||
|
return lui.Before(*luj)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildCacheWrite renders the context for a list of containers
|
||||||
|
func BuildCacheWrite(ctx Context, buildCaches []*types.BuildCache) error {
|
||||||
|
render := func(format func(subContext subContext) error) error {
|
||||||
|
buildCacheSort(buildCaches)
|
||||||
|
for _, bc := range buildCaches {
|
||||||
|
err := format(&buildCacheContext{trunc: ctx.Trunc, v: bc})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ctx.Write(newBuildCacheContext(), render)
|
||||||
|
}
|
||||||
|
|
||||||
|
type buildCacheHeaderContext map[string]string
|
||||||
|
|
||||||
|
type buildCacheContext struct {
|
||||||
|
HeaderContext
|
||||||
|
trunc bool
|
||||||
|
v *types.BuildCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBuildCacheContext() *buildCacheContext {
|
||||||
|
buildCacheCtx := buildCacheContext{}
|
||||||
|
buildCacheCtx.header = buildCacheHeaderContext{
|
||||||
|
"ID": cacheIDHeader,
|
||||||
|
"Parent": parentHeader,
|
||||||
|
"Type": typeHeader,
|
||||||
|
"Size": sizeHeader,
|
||||||
|
"CreatedSince": createdSinceHeader,
|
||||||
|
"LastUsedSince": lastUsedSinceHeader,
|
||||||
|
"UsageCount": usageCountHeader,
|
||||||
|
"InUse": inUseHeader,
|
||||||
|
"Shared": sharedHeader,
|
||||||
|
"Description": descriptionHeader,
|
||||||
|
}
|
||||||
|
return &buildCacheCtx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *buildCacheContext) MarshalJSON() ([]byte, error) {
|
||||||
|
return marshalJSON(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *buildCacheContext) ID() string {
|
||||||
|
id := c.v.ID
|
||||||
|
if c.trunc {
|
||||||
|
id = stringid.TruncateID(c.v.ID)
|
||||||
|
}
|
||||||
|
if c.v.InUse {
|
||||||
|
return id + "*"
|
||||||
|
}
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *buildCacheContext) Parent() string {
|
||||||
|
if c.trunc {
|
||||||
|
return stringid.TruncateID(c.v.Parent)
|
||||||
|
}
|
||||||
|
return c.v.Parent
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *buildCacheContext) Type() string {
|
||||||
|
return c.v.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *buildCacheContext) Description() string {
|
||||||
|
return c.v.Description
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *buildCacheContext) Size() string {
|
||||||
|
return units.HumanSizeWithPrecision(float64(c.v.Size), 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *buildCacheContext) CreatedSince() string {
|
||||||
|
return units.HumanDuration(time.Now().UTC().Sub(c.v.CreatedAt)) + " ago"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *buildCacheContext) LastUsedSince() string {
|
||||||
|
if c.v.LastUsedAt == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return units.HumanDuration(time.Now().UTC().Sub(*c.v.LastUsedAt)) + " ago"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *buildCacheContext) UsageCount() string {
|
||||||
|
return fmt.Sprintf("%d", c.v.UsageCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *buildCacheContext) InUse() string {
|
||||||
|
return fmt.Sprintf("%t", c.v.InUse)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *buildCacheContext) Shared() string {
|
||||||
|
return fmt.Sprintf("%t", c.v.Shared)
|
||||||
|
}
|
|
@ -12,22 +12,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultDiskUsageImageTableFormat = "table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.CreatedSince}} ago\t{{.VirtualSize}}\t{{.SharedSize}}\t{{.UniqueSize}}\t{{.Containers}}"
|
defaultDiskUsageImageTableFormat = "table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.CreatedSince}}\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}}"
|
defaultDiskUsageContainerTableFormat = "table {{.ID}}\t{{.Image}}\t{{.Command}}\t{{.LocalVolumes}}\t{{.Size}}\t{{.RunningFor}}\t{{.Status}}\t{{.Names}}"
|
||||||
defaultDiskUsageVolumeTableFormat = "table {{.Name}}\t{{.Links}}\t{{.Size}}"
|
defaultDiskUsageVolumeTableFormat = "table {{.Name}}\t{{.Links}}\t{{.Size}}"
|
||||||
defaultDiskUsageTableFormat = "table {{.Type}}\t{{.TotalCount}}\t{{.Active}}\t{{.Size}}\t{{.Reclaimable}}"
|
defaultDiskUsageTableFormat = "table {{.Type}}\t{{.TotalCount}}\t{{.Active}}\t{{.Size}}\t{{.Reclaimable}}"
|
||||||
defaultBuildCacheVerboseFormat = `
|
defaultDiskUsageBuildCacheTableFormat = "table {{.ID}}\t{{.Type}}\t{{.Size}}\t{{.CreatedSince}}\t{{.LastUsedSince}}\t{{.UsageCount}}\t{{.Shared}}"
|
||||||
ID: {{.ID}}
|
|
||||||
Parent: {{.Parent}}
|
|
||||||
Type: {{.Type}}
|
|
||||||
Description: {{.Description}}
|
|
||||||
Size: {{.Size}}
|
|
||||||
CreatedAt: {{.CreatedAt}}
|
|
||||||
LastUsedAt: {{.LastUsedAt}}
|
|
||||||
UsageCount: {{.UsageCount}}
|
|
||||||
InUse: {{.InUse}}
|
|
||||||
Shared: {{.Shared}}
|
|
||||||
`
|
|
||||||
|
|
||||||
typeHeader = "TYPE"
|
typeHeader = "TYPE"
|
||||||
totalHeader = "TOTAL"
|
totalHeader = "TOTAL"
|
||||||
|
@ -35,7 +24,7 @@ Shared: {{.Shared}}
|
||||||
reclaimableHeader = "RECLAIMABLE"
|
reclaimableHeader = "RECLAIMABLE"
|
||||||
containersHeader = "CONTAINERS"
|
containersHeader = "CONTAINERS"
|
||||||
sharedSizeHeader = "SHARED SIZE"
|
sharedSizeHeader = "SHARED SIZE"
|
||||||
uniqueSizeHeader = "UNIQUE SiZE"
|
uniqueSizeHeader = "UNIQUE SIZE"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DiskUsageContext contains disk usage specific information required by the formatter, encapsulate a Context struct.
|
// DiskUsageContext contains disk usage specific information required by the formatter, encapsulate a Context struct.
|
||||||
|
@ -59,7 +48,6 @@ func (ctx *DiskUsageContext) startSubsection(format string) (*template.Template,
|
||||||
return ctx.parseFormat()
|
return ctx.parseFormat()
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// NewDiskUsageFormat returns a format for rendering an DiskUsageContext
|
// NewDiskUsageFormat returns a format for rendering an DiskUsageContext
|
||||||
func NewDiskUsageFormat(source string) Format {
|
func NewDiskUsageFormat(source string) Format {
|
||||||
switch source {
|
switch source {
|
||||||
|
@ -132,6 +120,7 @@ func (ctx *DiskUsageContext) Write() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint: gocyclo
|
||||||
func (ctx *DiskUsageContext) verboseWrite() error {
|
func (ctx *DiskUsageContext) verboseWrite() error {
|
||||||
// First images
|
// First images
|
||||||
tmpl, err := ctx.startSubsection(defaultDiskUsageImageTableFormat)
|
tmpl, err := ctx.startSubsection(defaultDiskUsageImageTableFormat)
|
||||||
|
@ -199,11 +188,17 @@ func (ctx *DiskUsageContext) verboseWrite() error {
|
||||||
// And build cache
|
// And build cache
|
||||||
fmt.Fprintf(ctx.Output, "\nBuild cache usage: %s\n\n", units.HumanSize(float64(ctx.BuilderSize)))
|
fmt.Fprintf(ctx.Output, "\nBuild cache usage: %s\n\n", units.HumanSize(float64(ctx.BuilderSize)))
|
||||||
|
|
||||||
t := template.Must(template.New("buildcache").Parse(defaultBuildCacheVerboseFormat))
|
tmpl, err = ctx.startSubsection(defaultDiskUsageBuildCacheTableFormat)
|
||||||
|
if err != nil {
|
||||||
for _, v := range ctx.BuildCache {
|
return err
|
||||||
t.Execute(ctx.Output, *v)
|
|
||||||
}
|
}
|
||||||
|
buildCacheSort(ctx.BuildCache)
|
||||||
|
for _, v := range ctx.BuildCache {
|
||||||
|
if err := ctx.contextFormat(tmpl, &buildCacheContext{v: v, trunc: true}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.postFormat(tmpl, newBuildCacheContext())
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,11 +32,11 @@ Build Cache 0 0 0B
|
||||||
DiskUsageContext{Verbose: true},
|
DiskUsageContext{Verbose: true},
|
||||||
`Images space usage:
|
`Images space usage:
|
||||||
|
|
||||||
REPOSITORY TAG IMAGE ID CREATED ago SIZE SHARED SIZE UNIQUE SiZE CONTAINERS
|
REPOSITORY TAG IMAGE ID CREATED SIZE SHARED SIZE UNIQUE SIZE CONTAINERS
|
||||||
|
|
||||||
Containers space usage:
|
Containers space usage:
|
||||||
|
|
||||||
CONTAINER ID IMAGE COMMAND LOCAL VOLUMES SIZE CREATED ago STATUS NAMES
|
CONTAINER ID IMAGE COMMAND LOCAL VOLUMES SIZE CREATED STATUS NAMES
|
||||||
|
|
||||||
Local Volumes space usage:
|
Local Volumes space usage:
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ VOLUME NAME LINKS SIZE
|
||||||
|
|
||||||
Build cache usage: 0B
|
Build cache usage: 0B
|
||||||
|
|
||||||
|
CACHE ID TYPE SIZE CREATED LAST USED USAGE SHARED
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
// Errors
|
// Errors
|
||||||
|
|
|
@ -52,13 +52,20 @@ func runDiskUsage(dockerCli command.Cli, opts diskUsageOptions) error {
|
||||||
format = formatter.TableFormatKey
|
format = formatter.TableFormatKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var bsz int64
|
||||||
|
for _, bc := range du.BuildCache {
|
||||||
|
if !bc.Shared {
|
||||||
|
bsz += bc.Size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
duCtx := formatter.DiskUsageContext{
|
duCtx := formatter.DiskUsageContext{
|
||||||
Context: formatter.Context{
|
Context: formatter.Context{
|
||||||
Output: dockerCli.Out(),
|
Output: dockerCli.Out(),
|
||||||
Format: formatter.NewDiskUsageFormat(format),
|
Format: formatter.NewDiskUsageFormat(format),
|
||||||
},
|
},
|
||||||
LayersSize: du.LayersSize,
|
LayersSize: du.LayersSize,
|
||||||
BuilderSize: du.BuilderSize,
|
BuilderSize: bsz,
|
||||||
BuildCache: du.BuildCache,
|
BuildCache: du.BuildCache,
|
||||||
Images: du.Images,
|
Images: du.Images,
|
||||||
Containers: du.Containers,
|
Containers: du.Containers,
|
||||||
|
|
Loading…
Reference in New Issue