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

Rework docker app image ls and add the --digests flag #709

Merged
merged 3 commits into from
Nov 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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