mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Add format to the docker system df command
Signed-off-by: Boaz Shuster <ripcurld.github@gmail.com>
This commit is contained in:
parent
9518a71cef
commit
985a9c7047
4 changed files with 168 additions and 14 deletions
|
@ -45,15 +45,33 @@ func (ctx *DiskUsageContext) startSubsection(format string) (*template.Template,
|
||||||
return ctx.parseFormat()
|
return ctx.parseFormat()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *DiskUsageContext) Write() {
|
//
|
||||||
|
// NewDiskUsageFormat returns a format for rendering an DiskUsageContext
|
||||||
|
func NewDiskUsageFormat(source string) Format {
|
||||||
|
switch source {
|
||||||
|
case TableFormatKey:
|
||||||
|
format := defaultDiskUsageTableFormat
|
||||||
|
return Format(format)
|
||||||
|
case RawFormatKey:
|
||||||
|
format := `type: {{.Type}}
|
||||||
|
total: {{.TotalCount}}
|
||||||
|
active: {{.Active}}
|
||||||
|
size: {{.Size}}
|
||||||
|
reclaimable: {{.Reclaimable}}
|
||||||
|
`
|
||||||
|
return Format(format)
|
||||||
|
}
|
||||||
|
return Format(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *DiskUsageContext) Write() (err error) {
|
||||||
if ctx.Verbose == false {
|
if ctx.Verbose == false {
|
||||||
ctx.buffer = bytes.NewBufferString("")
|
ctx.buffer = bytes.NewBufferString("")
|
||||||
ctx.Format = defaultDiskUsageTableFormat
|
|
||||||
ctx.preFormat()
|
ctx.preFormat()
|
||||||
|
|
||||||
tmpl, err := ctx.parseFormat()
|
tmpl, err := ctx.parseFormat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ctx.contextFormat(tmpl, &diskUsageImagesContext{
|
err = ctx.contextFormat(tmpl, &diskUsageImagesContext{
|
||||||
|
@ -61,20 +79,20 @@ func (ctx *DiskUsageContext) Write() {
|
||||||
images: ctx.Images,
|
images: ctx.Images,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
err = ctx.contextFormat(tmpl, &diskUsageContainersContext{
|
err = ctx.contextFormat(tmpl, &diskUsageContainersContext{
|
||||||
containers: ctx.Containers,
|
containers: ctx.Containers,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ctx.contextFormat(tmpl, &diskUsageVolumesContext{
|
err = ctx.contextFormat(tmpl, &diskUsageVolumesContext{
|
||||||
volumes: ctx.Volumes,
|
volumes: ctx.Volumes,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
diskUsageContainersCtx := diskUsageContainersContext{containers: []*types.Container{}}
|
diskUsageContainersCtx := diskUsageContainersContext{containers: []*types.Container{}}
|
||||||
|
@ -87,7 +105,7 @@ func (ctx *DiskUsageContext) Write() {
|
||||||
}
|
}
|
||||||
ctx.postFormat(tmpl, &diskUsageContainersCtx)
|
ctx.postFormat(tmpl, &diskUsageContainersCtx)
|
||||||
|
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// First images
|
// First images
|
||||||
|
@ -158,6 +176,7 @@ func (ctx *DiskUsageContext) Write() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.postFormat(tmpl, newVolumeContext())
|
ctx.postFormat(tmpl, newVolumeContext())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
type diskUsageImagesContext struct {
|
type diskUsageImagesContext struct {
|
||||||
|
|
|
@ -8,13 +8,17 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDiskUsageContextFormatWrite(t *testing.T) {
|
func TestDiskUsageContextFormatWrite(t *testing.T) {
|
||||||
// Check default output format (verbose and non-verbose mode) for table headers
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
context DiskUsageContext
|
context DiskUsageContext
|
||||||
expected string
|
expected string
|
||||||
}{
|
}{
|
||||||
|
// Check default output format (verbose and non-verbose mode) for table headers
|
||||||
{
|
{
|
||||||
DiskUsageContext{Verbose: false},
|
DiskUsageContext{
|
||||||
|
Context: Context{
|
||||||
|
Format: NewDiskUsageFormat("table"),
|
||||||
|
},
|
||||||
|
Verbose: false},
|
||||||
`TYPE TOTAL ACTIVE SIZE RECLAIMABLE
|
`TYPE TOTAL ACTIVE SIZE RECLAIMABLE
|
||||||
Images 0 0 0B 0B
|
Images 0 0 0B 0B
|
||||||
Containers 0 0 0B 0B
|
Containers 0 0 0B 0B
|
||||||
|
@ -34,6 +38,77 @@ CONTAINER ID IMAGE COMMAND LOCAL VOLUMES
|
||||||
Local Volumes space usage:
|
Local Volumes space usage:
|
||||||
|
|
||||||
VOLUME NAME LINKS SIZE
|
VOLUME NAME LINKS SIZE
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
// Errors
|
||||||
|
{
|
||||||
|
DiskUsageContext{
|
||||||
|
Context: Context{
|
||||||
|
Format: "{{InvalidFunction}}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
`Template parsing error: template: :1: function "InvalidFunction" not defined
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DiskUsageContext{
|
||||||
|
Context: Context{
|
||||||
|
Format: "{{nil}}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
`Template parsing error: template: :1:2: executing "" at <nil>: nil is not a command
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
// Table Format
|
||||||
|
{
|
||||||
|
DiskUsageContext{
|
||||||
|
Context: Context{
|
||||||
|
Format: NewDiskUsageFormat("table"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
`TYPE TOTAL ACTIVE SIZE RECLAIMABLE
|
||||||
|
Images 0 0 0B 0B
|
||||||
|
Containers 0 0 0B 0B
|
||||||
|
Local Volumes 0 0 0B 0B
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DiskUsageContext{
|
||||||
|
Context: Context{
|
||||||
|
Format: NewDiskUsageFormat("table {{.Type}}\t{{.Active}}"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
`TYPE ACTIVE
|
||||||
|
Images 0
|
||||||
|
Containers 0
|
||||||
|
Local Volumes 0
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
// Raw Format
|
||||||
|
{
|
||||||
|
DiskUsageContext{
|
||||||
|
Context: Context{
|
||||||
|
Format: NewDiskUsageFormat("raw"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
`type: Images
|
||||||
|
total: 0
|
||||||
|
active: 0
|
||||||
|
size: 0B
|
||||||
|
reclaimable: 0B
|
||||||
|
|
||||||
|
type: Containers
|
||||||
|
total: 0
|
||||||
|
active: 0
|
||||||
|
size: 0B
|
||||||
|
reclaimable: 0B
|
||||||
|
|
||||||
|
type: Local Volumes
|
||||||
|
total: 0
|
||||||
|
active: 0
|
||||||
|
size: 0B
|
||||||
|
reclaimable: 0B
|
||||||
|
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -41,7 +116,10 @@ VOLUME NAME LINKS SIZE
|
||||||
for _, testcase := range cases {
|
for _, testcase := range cases {
|
||||||
out := bytes.NewBufferString("")
|
out := bytes.NewBufferString("")
|
||||||
testcase.context.Output = out
|
testcase.context.Output = out
|
||||||
testcase.context.Write()
|
if err := testcase.context.Write(); err != nil {
|
||||||
assert.Equal(t, out.String(), testcase.expected)
|
assert.Equal(t, err.Error(), testcase.expected)
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, out.String(), testcase.expected)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package system
|
package system
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
"github.com/docker/docker/cli"
|
"github.com/docker/docker/cli"
|
||||||
"github.com/docker/docker/cli/command"
|
"github.com/docker/docker/cli/command"
|
||||||
"github.com/docker/docker/cli/command/formatter"
|
"github.com/docker/docker/cli/command/formatter"
|
||||||
|
@ -10,6 +12,7 @@ import (
|
||||||
|
|
||||||
type diskUsageOptions struct {
|
type diskUsageOptions struct {
|
||||||
verbose bool
|
verbose bool
|
||||||
|
format string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDiskUsageCommand creates a new cobra.Command for `docker df`
|
// NewDiskUsageCommand creates a new cobra.Command for `docker df`
|
||||||
|
@ -29,19 +32,30 @@ func NewDiskUsageCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||||
flags := cmd.Flags()
|
flags := cmd.Flags()
|
||||||
|
|
||||||
flags.BoolVarP(&opts.verbose, "verbose", "v", false, "Show detailed information on space usage")
|
flags.BoolVarP(&opts.verbose, "verbose", "v", false, "Show detailed information on space usage")
|
||||||
|
flags.StringVar(&opts.format, "format", "", "Pretty-print images using a Go template")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func runDiskUsage(dockerCli *command.DockerCli, opts diskUsageOptions) error {
|
func runDiskUsage(dockerCli *command.DockerCli, opts diskUsageOptions) error {
|
||||||
|
if opts.verbose && len(opts.format) != 0 {
|
||||||
|
return errors.New("the verbose and the format options conflict")
|
||||||
|
}
|
||||||
|
|
||||||
du, err := dockerCli.Client().DiskUsage(context.Background())
|
du, err := dockerCli.Client().DiskUsage(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
format := opts.format
|
||||||
|
if len(format) == 0 {
|
||||||
|
format = formatter.TableFormatKey
|
||||||
|
}
|
||||||
|
|
||||||
duCtx := formatter.DiskUsageContext{
|
duCtx := formatter.DiskUsageContext{
|
||||||
Context: formatter.Context{
|
Context: formatter.Context{
|
||||||
Output: dockerCli.Out(),
|
Output: dockerCli.Out(),
|
||||||
|
Format: formatter.NewDiskUsageFormat(format),
|
||||||
},
|
},
|
||||||
LayersSize: du.LayersSize,
|
LayersSize: du.LayersSize,
|
||||||
Images: du.Images,
|
Images: du.Images,
|
||||||
|
@ -50,7 +64,5 @@ func runDiskUsage(dockerCli *command.DockerCli, opts diskUsageOptions) error {
|
||||||
Verbose: opts.verbose,
|
Verbose: opts.verbose,
|
||||||
}
|
}
|
||||||
|
|
||||||
duCtx.Write()
|
return duCtx.Write()
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,6 +86,51 @@ volumes or in systems where some images, containers, or volumes have very large
|
||||||
filesystems with many files. You should also be careful not to run this command
|
filesystems with many files. You should also be careful not to run this command
|
||||||
in systems where performance is critical.
|
in systems where performance is critical.
|
||||||
|
|
||||||
|
## Format the output
|
||||||
|
|
||||||
|
The formatting option (`--format`) pretty prints the disk usage output
|
||||||
|
using a Go template.
|
||||||
|
|
||||||
|
Valid placeholders for the Go template are listed below:
|
||||||
|
|
||||||
|
| Placeholder | Description |
|
||||||
|
| -------------- | ------------------------------------------ |
|
||||||
|
| `.Type` | `Images`, `Containers` and `Local Volumes` |
|
||||||
|
| `.TotalCount` | Total number of items |
|
||||||
|
| `.Active` | Number of active items |
|
||||||
|
| `.Size` | Available size |
|
||||||
|
| `.Reclaimable` | Reclaimable size |
|
||||||
|
|
||||||
|
When using the `--format` option, the `system df` command outputs
|
||||||
|
the data exactly as the template declares or, when using the
|
||||||
|
`table` directive, will include column headers as well.
|
||||||
|
|
||||||
|
The following example uses a template without headers and outputs the
|
||||||
|
`Type` and `TotalCount` entries separated by a colon:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ docker system df --format "{{.Type}}: {{.TotalCount}}"
|
||||||
|
|
||||||
|
Images: 2
|
||||||
|
Containers: 4
|
||||||
|
Local Volumes: 1
|
||||||
|
```
|
||||||
|
|
||||||
|
To list the disk usage with size and reclaimable size in a table format you
|
||||||
|
can use:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ docker system df --format "table {{.Type}}\t{{.Size}}\t{{.Reclaimable}}"
|
||||||
|
|
||||||
|
TYPE SIZE RECLAIMABLE
|
||||||
|
Images 2.547 GB 2.342 GB (91%)
|
||||||
|
Containers 0 B 0 B
|
||||||
|
Local Volumes 150.3 MB 150.3 MB (100%)
|
||||||
|
<Paste>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note** the format option is meaningless when verbose is true.
|
||||||
|
|
||||||
## Related commands
|
## Related commands
|
||||||
* [system prune](system_prune.md)
|
* [system prune](system_prune.md)
|
||||||
* [container prune](container_prune.md)
|
* [container prune](container_prune.md)
|
||||||
|
|
Loading…
Reference in a new issue