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 #709 from zappy-shu/add-digest-flag-to-ls
Browse files Browse the repository at this point in the history
Rework docker app image ls and add the --digests flag
  • Loading branch information
chris-crone committed Nov 6, 2019
2 parents f141d47 + 24ced43 commit 8acd0a8
Show file tree
Hide file tree
Showing 3 changed files with 205 additions and 91 deletions.
116 changes: 67 additions & 49 deletions e2e/images_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import (
"bufio"
"fmt"
"path/filepath"
"regexp"
"strings"
"testing"

"gotest.tools/assert"
"gotest.tools/fs"
"gotest.tools/icmd"
)

Expand All @@ -22,10 +22,20 @@ func insertBundles(t *testing.T, cmd icmd.Cmd, info dindSwarmAndRegistryInfo) {
icmd.RunCmd(cmd).Assert(t, icmd.Success)
}

func assertImageListOutput(t *testing.T, cmd icmd.Cmd, expected string) {
result := icmd.RunCmd(cmd).Assert(t, icmd.Success)
match, _ := regexp.MatchString(expected, result.Stdout())
assert.Assert(t, match)
}

func expectImageListOutput(t *testing.T, cmd icmd.Cmd, output string) {
cmd.Command = dockerCli.Command("app", "image", "ls")
result := icmd.RunCmd(cmd).Assert(t, icmd.Success)
assert.Equal(t, result.Stdout(), output)
assertImageListOutput(t, cmd, output)
}

func expectImageListDigestsOutput(t *testing.T, cmd icmd.Cmd, output string) {
cmd.Command = dockerCli.Command("app", "image", "ls", "--digests")
assertImageListOutput(t, cmd, output)
}

func verifyImageIDListOutput(t *testing.T, cmd icmd.Cmd, count int, distinct int) {
Expand All @@ -48,36 +58,44 @@ func verifyImageIDListOutput(t *testing.T, cmd icmd.Cmd, count int, distinct int
func TestImageList(t *testing.T) {
runWithDindSwarmAndRegistry(t, func(info dindSwarmAndRegistryInfo) {
cmd := info.configuredCmd
dir := fs.NewDir(t, "")
defer dir.Remove()

insertBundles(t, cmd, info)

expected := `APP IMAGE APP NAME
%s push-pull
a-simple-app:latest simple
b-simple-app:latest simple
expected := `REPOSITORY TAG APP IMAGE ID APP NAME
%s latest [a-f0-9]{12} push-pull
a-simple-app latest [a-f0-9]{12} simple
b-simple-app latest [a-f0-9]{12} simple
`
expectedOutput := fmt.Sprintf(expected, info.registryAddress+"/c-myapp:latest")
expectedOutput := fmt.Sprintf(expected, info.registryAddress+"/c-myapp")
expectImageListOutput(t, cmd, expectedOutput)
})
}

func TestImageListQuiet(t *testing.T) {
runWithDindSwarmAndRegistry(t, func(info dindSwarmAndRegistryInfo) {
cmd := info.configuredCmd
dir := fs.NewDir(t, "")
defer dir.Remove()
insertBundles(t, cmd, info)
verifyImageIDListOutput(t, cmd, 3, 2)
})
}

func TestImageListDigests(t *testing.T) {
runWithDindSwarmAndRegistry(t, func(info dindSwarmAndRegistryInfo) {
cmd := info.configuredCmd
insertBundles(t, cmd, info)
expected := `REPOSITORY TAG DIGEST APP IMAGE ID APP NAME
%s latest <none> [a-f0-9]{12} push-pull
a-simple-app latest <none> [a-f0-9]{12} simple
b-simple-app latest <none> [a-f0-9]{12} simple
`
expectedOutput := fmt.Sprintf(expected, info.registryAddress+"/c-myapp")
expectImageListDigestsOutput(t, cmd, expectedOutput)
})
}

func TestImageRm(t *testing.T) {
runWithDindSwarmAndRegistry(t, func(info dindSwarmAndRegistryInfo) {
cmd := info.configuredCmd
dir := fs.NewDir(t, "")
defer dir.Remove()

insertBundles(t, cmd, info)

Expand All @@ -100,7 +118,7 @@ Deleted: b-simple-app:latest`,
Err: `b-simple-app:latest: reference not found`,
})

expectedOutput := "APP IMAGE APP NAME\n"
expectedOutput := "REPOSITORY TAG APP IMAGE ID APP NAME\n"
expectImageListOutput(t, cmd, expectedOutput)
})
}
Expand All @@ -118,8 +136,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 := `APP IMAGE APP NAME
a-simple-app:latest simple
singleImageExpectation := `REPOSITORY TAG APP IMAGE ID APP NAME
a-simple-app latest [a-f0-9]{12} simple
`
expectImageListOutput(t, cmd, singleImageExpectation)

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

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

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

// 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, `APP IMAGE APP NAME
a-simple-app:0.1 simple
a-simple-app:latest simple
b-simple-app:0.2 simple
b-simple-app:latest simple
c-simple-app:latest simple
expectImageListOutput(t, cmd, `REPOSITORY TAG APP IMAGE ID APP NAME
a-simple-app 0.1 [a-f0-9]{12} simple
a-simple-app latest [a-f0-9]{12} simple
b-simple-app 0.2 [a-f0-9]{12} simple
b-simple-app latest [a-f0-9]{12} simple
c-simple-app latest [a-f0-9]{12} simple
`)

// 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, `APP IMAGE APP NAME
a-simple-app:0.1 simple
a-simple-app:latest simple
b-simple-app:0.2 simple
b-simple-app:latest simple
c-simple-app:latest simple
push-pull:latest push-pull
expectImageListOutput(t, cmd, `REPOSITORY TAG APP IMAGE ID APP NAME
a-simple-app 0.1 [a-f0-9]{12} simple
a-simple-app latest [a-f0-9]{12} simple
b-simple-app 0.2 [a-f0-9]{12} simple
b-simple-app latest [a-f0-9]{12} simple
c-simple-app latest [a-f0-9]{12} simple
push-pull latest [a-f0-9]{12} push-pull
`)

// 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, `APP IMAGE APP NAME
a-simple-app:0.1 simple
a-simple-app:latest simple
b-simple-app:0.2 push-pull
b-simple-app:latest simple
c-simple-app:latest simple
push-pull:latest push-pull
expectImageListOutput(t, cmd, `REPOSITORY TAG APP IMAGE ID APP NAME
a-simple-app 0.1 [a-f0-9]{12} simple
a-simple-app latest [a-f0-9]{12} simple
b-simple-app 0.2 [a-f0-9]{12} push-pull
b-simple-app latest [a-f0-9]{12} simple
c-simple-app latest [a-f0-9]{12} simple
push-pull latest [a-f0-9]{12} push-pull
`)
})
}
89 changes: 64 additions & 25 deletions internal/commands/image/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ import (
)

type imageListOption struct {
quiet bool
quiet bool
digests bool
}

type imageListColumn struct {
header string
value func(p pkg) string
}

func listCmd(dockerCli command.Cli) *cobra.Command {
Expand All @@ -42,6 +48,7 @@ func listCmd(dockerCli command.Cli) *cobra.Command {
}
flags := cmd.Flags()
flags.BoolVarP(&options.quiet, "quiet", "q", false, "Only show numeric IDs")
flags.BoolVarP(&options.digests, "digests", "", false, "Show image digests")

return cmd
}
Expand All @@ -60,7 +67,7 @@ func runList(dockerCli command.Cli, options imageListOption, bundleStore store.B
if options.quiet {
return printImageIDs(dockerCli, pkgs)
}
return printImages(dockerCli, pkgs)
return printImages(dockerCli, pkgs, options)
}

func getPackages(bundleStore store.BundleStore, references []reference.Reference) ([]pkg, error) {
Expand All @@ -82,12 +89,12 @@ func getPackages(bundleStore store.BundleStore, references []reference.Reference
return packages, nil
}

func printImages(dockerCli command.Cli, refs []pkg) error {
func printImages(dockerCli command.Cli, refs []pkg, options imageListOption) error {
w := tabwriter.NewWriter(dockerCli.Out(), 0, 0, 1, ' ', 0)

printHeaders(w)
listColumns := getImageListColumns(options)
printHeaders(w, listColumns)
for _, ref := range refs {
printValues(w, ref)
printValues(w, ref, listColumns)
}

return w.Flush()
Expand All @@ -97,49 +104,81 @@ func printImageIDs(dockerCli command.Cli, refs []pkg) error {
var buf bytes.Buffer

for _, ref := range refs {
id, ok := ref.ref.(store.ID)
if !ok {
var err error
id, err = store.FromBundle(ref.bundle)
if err != nil {
return err
}
id, err := getImageID(ref)
if err != nil {
return err
}
fmt.Fprintln(&buf, stringid.TruncateID(id.String()))
fmt.Fprintln(&buf, id)
}
fmt.Fprint(dockerCli.Out(), buf.String())
return nil
}

func printHeaders(w io.Writer) {
func getImageID(p pkg) (string, error) {
id, ok := p.ref.(store.ID)
if !ok {
var err error
id, err = store.FromBundle(p.bundle)
if err != nil {
return "", err
}
}
return stringid.TruncateID(id.String()), nil
}

func printHeaders(w io.Writer, listColumns []imageListColumn) {
var headers []string
for _, column := range listColumns {
headers = append(headers, column.header)
}
fmt.Fprintln(w, strings.Join(headers, "\t"))
}

func printValues(w io.Writer, ref pkg) {
func printValues(w io.Writer, ref pkg, listColumns []imageListColumn) {
var values []string
for _, column := range listColumns {
values = append(values, column.value(ref))
}
fmt.Fprintln(w, strings.Join(values, "\t"))
}

var (
listColumns = []struct {
header string
value func(p pkg) string
}{
{"APP IMAGE", func(p pkg) string {
func getImageListColumns(options imageListOption) []imageListColumn {
columns := []imageListColumn{
{"REPOSITORY", func(p pkg) string {
if n, ok := p.ref.(reference.Named); ok {
return reference.FamiliarName(n)
}
return reference.FamiliarString(p.ref)
}},
{"APP NAME", func(p pkg) string {
return p.bundle.Name
{"TAG", func(p pkg) string {
if t, ok := p.ref.(reference.Tagged); ok {
return t.Tag()
}
return "<none>"
}},
}
)
if options.digests {
columns = append(columns, imageListColumn{"DIGEST", func(p pkg) string {
if t, ok := p.ref.(reference.Digested); ok {
return t.Digest().String()
}
return "<none>"
}})
}
columns = append(columns,
imageListColumn{"APP IMAGE ID", func(p pkg) string {
id, err := getImageID(p)
if err != nil {
return ""
}
return id
}},
imageListColumn{"APP NAME", func(p pkg) string {
return p.bundle.Name
}},
)
return columns
}

type pkg struct {
ref reference.Reference
Expand Down
Loading

0 comments on commit 8acd0a8

Please sign in to comment.