diff --git a/command/formatter/disk_usage.go b/command/formatter/disk_usage.go index 7170411e1b..07e39826ed 100644 --- a/command/formatter/disk_usage.go +++ b/command/formatter/disk_usage.go @@ -45,15 +45,33 @@ func (ctx *DiskUsageContext) startSubsection(format string) (*template.Template, 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 { ctx.buffer = bytes.NewBufferString("") - ctx.Format = defaultDiskUsageTableFormat ctx.preFormat() tmpl, err := ctx.parseFormat() if err != nil { - return + return err } err = ctx.contextFormat(tmpl, &diskUsageImagesContext{ @@ -61,20 +79,20 @@ func (ctx *DiskUsageContext) Write() { images: ctx.Images, }) if err != nil { - return + return err } err = ctx.contextFormat(tmpl, &diskUsageContainersContext{ containers: ctx.Containers, }) if err != nil { - return + return err } err = ctx.contextFormat(tmpl, &diskUsageVolumesContext{ volumes: ctx.Volumes, }) if err != nil { - return + return err } diskUsageContainersCtx := diskUsageContainersContext{containers: []*types.Container{}} @@ -87,7 +105,7 @@ func (ctx *DiskUsageContext) Write() { } ctx.postFormat(tmpl, &diskUsageContainersCtx) - return + return err } // First images @@ -158,6 +176,7 @@ func (ctx *DiskUsageContext) Write() { } } ctx.postFormat(tmpl, newVolumeContext()) + return } type diskUsageImagesContext struct { diff --git a/command/formatter/disk_usage_test.go b/command/formatter/disk_usage_test.go index 318e1692be..7093cfe85a 100644 --- a/command/formatter/disk_usage_test.go +++ b/command/formatter/disk_usage_test.go @@ -8,13 +8,17 @@ import ( ) func TestDiskUsageContextFormatWrite(t *testing.T) { - // Check default output format (verbose and non-verbose mode) for table headers cases := []struct { context DiskUsageContext 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 Images 0 0 0B 0B Containers 0 0 0B 0B @@ -34,6 +38,77 @@ CONTAINER ID IMAGE COMMAND LOCAL VOLUMES Local Volumes space usage: 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 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 { out := bytes.NewBufferString("") testcase.context.Output = out - testcase.context.Write() - assert.Equal(t, out.String(), testcase.expected) + if err := testcase.context.Write(); err != nil { + assert.Equal(t, err.Error(), testcase.expected) + } else { + assert.Equal(t, out.String(), testcase.expected) + } } } diff --git a/command/system/df.go b/command/system/df.go index 9f712484aa..67b3b31d87 100644 --- a/command/system/df.go +++ b/command/system/df.go @@ -1,6 +1,8 @@ package system import ( + "errors" + "github.com/docker/docker/cli" "github.com/docker/docker/cli/command" "github.com/docker/docker/cli/command/formatter" @@ -10,6 +12,7 @@ import ( type diskUsageOptions struct { verbose bool + format string } // NewDiskUsageCommand creates a new cobra.Command for `docker df` @@ -29,19 +32,30 @@ func NewDiskUsageCommand(dockerCli *command.DockerCli) *cobra.Command { flags := cmd.Flags() 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 } 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()) if err != nil { return err } + format := opts.format + if len(format) == 0 { + format = formatter.TableFormatKey + } + duCtx := formatter.DiskUsageContext{ Context: formatter.Context{ Output: dockerCli.Out(), + Format: formatter.NewDiskUsageFormat(format), }, LayersSize: du.LayersSize, Images: du.Images, @@ -50,7 +64,5 @@ func runDiskUsage(dockerCli *command.DockerCli, opts diskUsageOptions) error { Verbose: opts.verbose, } - duCtx.Write() - - return nil + return duCtx.Write() }