Skip to content
This repository has been archived by the owner on Jun 13, 2021. It is now read-only.

Commit

Permalink
Merge pull request #743 from ndeloof/template
Browse files Browse the repository at this point in the history
Support format option in docker app ls
  • Loading branch information
silvin-lubecki authored Nov 20, 2019
2 parents 355ab4f + d5e1ff5 commit df7ae09
Show file tree
Hide file tree
Showing 6 changed files with 270 additions and 175 deletions.
2 changes: 2 additions & 0 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

87 changes: 44 additions & 43 deletions e2e/images_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,12 @@ func TestImageList(t *testing.T) {

insertBundles(t, cmd)

expected := `REPOSITORY TAG APP IMAGE ID APP NAME CREATED
a-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago
b-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago
my.registry:5000/c-myapp latest [a-f0-9]{12} push-pull [La-z0-9 ]+ ago
expected := `REPOSITORY TAG APP IMAGE ID APP NAME CREATED
a-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago[ ]*
b-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago[ ]*
my.registry:5000/c-myapp latest [a-f0-9]{12} push-pull [La-z0-9 ]+ ago[ ]*
`

expectImageListOutput(t, cmd, expected)
})
}
Expand All @@ -80,10 +81,10 @@ func TestImageListDigests(t *testing.T) {
runWithDindSwarmAndRegistry(t, func(info dindSwarmAndRegistryInfo) {
cmd := info.configuredCmd
insertBundles(t, cmd)
expected := `REPOSITORY TAG DIGEST APP IMAGE ID APP NAME CREATED
a-simple-app latest <none> [a-f0-9]{12} simple [La-z0-9 ]+ ago
b-simple-app latest <none> [a-f0-9]{12} simple [La-z0-9 ]+ ago
my.registry:5000/c-myapp latest <none> [a-f0-9]{12} push-pull [La-z0-9 ]+ ago
expected := `REPOSITORY TAG DIGEST APP IMAGE ID APP NAME CREATED
a-simple-app latest <none> [a-f0-9]{12} simple [La-z0-9 ]+ ago[ ]*
b-simple-app latest <none> [a-f0-9]{12} simple [La-z0-9 ]+ ago[ ]*
my.registry:5000/c-myapp latest <none> [a-f0-9]{12} push-pull [La-z0-9 ]+ ago[ ]*
`
expectImageListDigestsOutput(t, cmd, expected)
})
Expand Down Expand Up @@ -114,7 +115,7 @@ Deleted: b-simple-app:latest`,
Err: `b-simple-app:latest: reference not found`,
})

expectedOutput := "REPOSITORY TAG APP IMAGE ID APP NAME CREATED\n"
expectedOutput := "REPOSITORY TAG APP IMAGE ID APP NAME CREATED \n"
expectImageListOutput(t, cmd, expectedOutput)
})
}
Expand All @@ -132,8 +133,8 @@ func TestImageTag(t *testing.T) {
cmd.Command = dockerCli.Command("app", "build", "--tag", "a-simple-app", filepath.Join("testdata", "simple"))
icmd.RunCmd(cmd).Assert(t, icmd.Success)

singleImageExpectation := `REPOSITORY TAG APP IMAGE ID APP NAME CREATED
a-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago
singleImageExpectation := `REPOSITORY TAG APP IMAGE ID APP NAME CREATED[ ]*
a-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago[ ]*
`
expectImageListOutput(t, cmd, singleImageExpectation)

Expand Down Expand Up @@ -182,63 +183,63 @@ a-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago
// tag image with only names
dockerAppImageTag("a-simple-app", "b-simple-app")
icmd.RunCmd(cmd).Assert(t, icmd.Success)
expectImageListOutput(t, cmd, `REPOSITORY TAG APP IMAGE ID APP NAME CREATED
a-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago
b-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago
expectImageListOutput(t, cmd, `REPOSITORY TAG APP IMAGE ID APP NAME CREATED[ ]*
a-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago[ ]*
b-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago[ ]*
`)

// target tag
dockerAppImageTag("a-simple-app", "a-simple-app:0.1")
icmd.RunCmd(cmd).Assert(t, icmd.Success)
expectImageListOutput(t, cmd, `REPOSITORY TAG APP IMAGE ID APP NAME CREATED
a-simple-app 0.1 [a-f0-9]{12} simple [La-z0-9 ]+ ago
a-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago
b-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago
expectImageListOutput(t, cmd, `REPOSITORY TAG APP IMAGE ID APP NAME CREATED[ ]*
a-simple-app 0.1 [a-f0-9]{12} simple [La-z0-9 ]+ ago[ ]*
a-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago[ ]*
b-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago[ ]*
`)

// source tag
dockerAppImageTag("a-simple-app:0.1", "c-simple-app")
icmd.RunCmd(cmd).Assert(t, icmd.Success)
expectImageListOutput(t, cmd, `REPOSITORY TAG APP IMAGE ID APP NAME CREATED
a-simple-app 0.1 [a-f0-9]{12} simple [La-z0-9 ]+ ago
a-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago
b-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago
c-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago
expectImageListOutput(t, cmd, `REPOSITORY TAG APP IMAGE ID APP NAME CREATED[ ]*
a-simple-app 0.1 [a-f0-9]{12} simple [La-z0-9 ]+ ago[ ]*
a-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago[ ]*
b-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago[ ]*
c-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago[ ]*
`)

// source and target tags
dockerAppImageTag("a-simple-app:0.1", "b-simple-app:0.2")
icmd.RunCmd(cmd).Assert(t, icmd.Success)
expectImageListOutput(t, cmd, `REPOSITORY TAG APP IMAGE ID APP NAME CREATED
a-simple-app 0.1 [a-f0-9]{12} simple [La-z0-9 ]+ ago
a-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago
b-simple-app 0.2 [a-f0-9]{12} simple [La-z0-9 ]+ ago
b-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago
c-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago
expectImageListOutput(t, cmd, `REPOSITORY TAG APP IMAGE ID APP NAME CREATED[ ]*
a-simple-app 0.1 [a-f0-9]{12} simple [La-z0-9 ]+ ago[ ]*
a-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago[ ]*
b-simple-app 0.2 [a-f0-9]{12} simple [La-z0-9 ]+ ago[ ]*
b-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago[ ]*
c-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago[ ]*
`)

// given a new application
cmd.Command = dockerCli.Command("app", "build", "--tag", "push-pull", filepath.Join("testdata", "push-pull"))
icmd.RunCmd(cmd).Assert(t, icmd.Success)
expectImageListOutput(t, cmd, `REPOSITORY TAG APP IMAGE ID APP NAME CREATED
a-simple-app 0.1 [a-f0-9]{12} simple [La-z0-9 ]+ ago
a-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago
b-simple-app 0.2 [a-f0-9]{12} simple [La-z0-9 ]+ ago
b-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago
c-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago
push-pull latest [a-f0-9]{12} push-pull [La-z0-9 ]+ ago
expectImageListOutput(t, cmd, `REPOSITORY TAG APP IMAGE ID APP NAME CREATED[ ]*
a-simple-app 0.1 [a-f0-9]{12} simple [La-z0-9 ]+ ago[ ]*
a-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago[ ]*
b-simple-app 0.2 [a-f0-9]{12} simple [La-z0-9 ]+ ago[ ]*
b-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago[ ]*
c-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago[ ]*
push-pull latest [a-f0-9]{12} push-pull [La-z0-9 ]+ ago[ ]*
`)

// can be tagged to an existing tag
dockerAppImageTag("push-pull", "b-simple-app:0.2")
icmd.RunCmd(cmd).Assert(t, icmd.Success)
expectImageListOutput(t, cmd, `REPOSITORY TAG APP IMAGE ID APP NAME CREATED
a-simple-app 0.1 [a-f0-9]{12} simple [La-z0-9 ]+ ago
a-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago
b-simple-app 0.2 [a-f0-9]{12} push-pull [La-z0-9 ]+ ago
b-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago
c-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago
push-pull latest [a-f0-9]{12} push-pull [La-z0-9 ]+ ago
expectImageListOutput(t, cmd, `REPOSITORY TAG APP IMAGE ID APP NAME CREATED[ ]*
a-simple-app 0.1 [a-f0-9]{12} simple [La-z0-9 ]+ ago[ ]*
a-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago[ ]*
b-simple-app 0.2 [a-f0-9]{12} push-pull [La-z0-9 ]+ ago[ ]*
b-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago[ ]*
c-simple-app latest [a-f0-9]{12} simple [La-z0-9 ]+ ago[ ]*
push-pull latest [a-f0-9]{12} push-pull [La-z0-9 ]+ ago[ ]*
`)
})
}
126 changes: 126 additions & 0 deletions internal/commands/image/formatter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package image

import (
"time"

"github.com/docker/cli/cli/command/formatter"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/go-units"
)

const (
defaultImageTableFormat = "table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.Name}}\t{{if .CreatedSince }}{{.CreatedSince}}{{else}}N/A{{end}}\t"
defaultImageTableFormatWithDigest = "table {{.Repository}}\t{{.Tag}}\t{{.Digest}}\t{{.ID}}\t{{.Name}}\t\t{{if .CreatedSince }}{{.CreatedSince}}{{else}}N/A{{end}}\t"

imageIDHeader = "APP IMAGE ID"
repositoryHeader = "REPOSITORY"
tagHeader = "TAG"
digestHeader = "DIGEST"
imageNameHeader = "APP NAME"
)

// NewImageFormat returns a format for rendering an ImageContext
func NewImageFormat(source string, quiet bool, digest bool) formatter.Format {
switch source {
case formatter.TableFormatKey:
switch {
case quiet:
return formatter.DefaultQuietFormat
case digest:
return defaultImageTableFormatWithDigest
default:
return defaultImageTableFormat
}
}

format := formatter.Format(source)
if format.IsTable() && digest && !format.Contains("{{.Digest}}") {
format += "\t{{.Digest}}"
}
return format
}

// Write writes the formatter images using the ImageContext
func Write(ctx formatter.Context, images []imageDesc) error {
render := func(format func(subContext formatter.SubContext) error) error {
return imageFormat(ctx, images, format)
}
return ctx.Write(newImageContext(), render)
}

func imageFormat(ctx formatter.Context, images []imageDesc, format func(subContext formatter.SubContext) error) error {
for _, image := range images {
img := &imageContext{
trunc: ctx.Trunc,
i: image}
if err := format(img); err != nil {
return err
}
}
return nil
}

type imageContext struct {
formatter.HeaderContext
trunc bool
i imageDesc
}

func newImageContext() *imageContext {
imageCtx := imageContext{}
imageCtx.Header = formatter.SubHeaderContext{
"ID": imageIDHeader,
"Name": imageNameHeader,
"Repository": repositoryHeader,
"Tag": tagHeader,
"Digest": digestHeader,
"CreatedSince": formatter.CreatedSinceHeader,
}
return &imageCtx
}

func (c *imageContext) MarshalJSON() ([]byte, error) {
return formatter.MarshalJSON(c)
}

func (c *imageContext) ID() string {
if c.trunc {
return stringid.TruncateID(c.i.ID)
}
return c.i.ID
}

func (c *imageContext) Name() string {
if c.i.Name == "" {
return "<none>"
}
return c.i.Name
}

func (c *imageContext) Repository() string {
if c.i.Repository == "" {
return "<none>"
}
return c.i.Repository
}

func (c *imageContext) Tag() string {
if c.i.Tag == "" {
return "<none>"
}
return c.i.Tag
}

func (c *imageContext) Digest() string {
if c.i.Digest == "" {
return "<none>"
}
return c.i.Digest
}

func (c *imageContext) CreatedSince() string {
if c.i.Created.IsZero() {
return ""
}
return units.HumanDuration(time.Now().UTC().Sub(c.i.Created)) + " ago"
}
Loading

0 comments on commit df7ae09

Please sign in to comment.