From 4c4943c88ace9290bb11814e6cd090be3ea9f8a7 Mon Sep 17 00:00:00 2001 From: Nick Adcock Date: Tue, 29 Oct 2019 11:57:16 +0000 Subject: [PATCH] split image ls output to repo and tag Split the app image ls output up so that the REPOSITORY and TAG are separate columns, the same as the standard docker image ls. The APP IMAGE ID column is now a default column, same as the ID column in docker image ls. As this ID is randomly generated the e2e tests have been updated to use regexes when asserting the command output. Signed-off-by: Nick Adcock --- e2e/images_test.go | 103 ++++++++++++++------------- internal/commands/image/list.go | 50 +++++++++---- internal/commands/image/list_test.go | 65 +++++++++++------ 3 files changed, 134 insertions(+), 84 deletions(-) diff --git a/e2e/images_test.go b/e2e/images_test.go index 2a7c74f85..c53ae9209 100644 --- a/e2e/images_test.go +++ b/e2e/images_test.go @@ -4,6 +4,7 @@ import ( "bufio" "fmt" "path/filepath" + "regexp" "strings" "testing" @@ -21,16 +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") - result := icmd.RunCmd(cmd).Assert(t, icmd.Success) - assert.Equal(t, result.Stdout(), output) + assertImageListOutput(t, cmd, output) } func verifyImageIDListOutput(t *testing.T, cmd icmd.Cmd, count int, distinct int) { @@ -56,12 +61,12 @@ func TestImageList(t *testing.T) { 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) }) } @@ -78,12 +83,12 @@ func TestImageListDigests(t *testing.T) { runWithDindSwarmAndRegistry(t, func(info dindSwarmAndRegistryInfo) { cmd := info.configuredCmd insertBundles(t, cmd, info) - expected := `APP IMAGE DIGEST APP NAME -%s push-pull -a-simple-app:latest simple -b-simple-app:latest simple + expected := `REPOSITORY TAG DIGEST 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") expectImageListDigestsOutput(t, cmd, expectedOutput) }) } @@ -113,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) }) } @@ -131,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) @@ -181,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 `) }) } diff --git a/internal/commands/image/list.go b/internal/commands/image/list.go index 3aa57eea5..0dd71ec25 100644 --- a/internal/commands/image/list.go +++ b/internal/commands/image/list.go @@ -104,20 +104,28 @@ 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 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 { @@ -136,9 +144,18 @@ func printValues(w io.Writer, ref pkg, listColumns []imageListColumn) { func getImageListColumns(options imageListOption) []imageListColumn { columns := []imageListColumn{ - {"APP IMAGE", func(p pkg) string { + {"REPOSITORY", func(p pkg) string { + if n, ok := p.ref.(reference.Named); ok { + return reference.FamiliarName(n) + } return reference.FamiliarString(p.ref) }}, + {"TAG", func(p pkg) string { + if t, ok := p.ref.(reference.Tagged); ok { + return t.Tag() + } + return "" + }}, } if options.digests { columns = append(columns, imageListColumn{"DIGEST", func(p pkg) string { @@ -148,9 +165,18 @@ func getImageListColumns(options imageListOption) []imageListColumn { return "" }}) } - columns = append(columns, imageListColumn{"APP NAME", func(p pkg) string { - return p.bundle.Name - }}) + 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 } diff --git a/internal/commands/image/list_test.go b/internal/commands/image/list_test.go index 116c9077f..a64ee7a27 100644 --- a/internal/commands/image/list_test.go +++ b/internal/commands/image/list_test.go @@ -46,47 +46,66 @@ func (b *bundleStoreStubForListCmd) LookUp(refOrID string) (reference.Reference, return nil, nil } -func TestListWithQuietFlag(t *testing.T) { +func TestListCmd(t *testing.T) { ref, err := store.FromString("a855ac937f2ed375ba4396bbc49c4093e124da933acd2713fb9bc17d7562a087") assert.NilError(t, err) refs := []reference.Reference{ - ref, + parseReference(t, "foo/bar@sha256:b59492bb814012ca3d2ce0b6728242d96b4af41687cc82166a4b5d7f2d9fb865"), parseReference(t, "foo/bar:1.0"), + ref, } bundles := []bundle.Bundle{ - {}, + { + Name: "Digested App", + }, { Version: "1.0.0", SchemaVersion: "1.0.0", Name: "Foo App", }, + { + Name: "Quiet App", + }, } - expectedOutput := `a855ac937f2e -9aae408ee04f -` - testRunList(t, refs, bundles, imageListOption{quiet: true}, expectedOutput) -} -func TestListWithDigestsFlag(t *testing.T) { - refs := []reference.Reference{ - parseReference(t, "foo/bar@sha256:b59492bb814012ca3d2ce0b6728242d96b4af41687cc82166a4b5d7f2d9fb865"), - parseReference(t, "foo/bar:1.0"), - } - bundles := []bundle.Bundle{ + testCases := []struct { + name string + expectedOutput string + options imageListOption + }{ { - Name: "Digested App", + name: "TestList", + expectedOutput: `REPOSITORY TAG APP IMAGE ID APP NAME +foo/bar 3f825b2d0657 Digested App +foo/bar 1.0 9aae408ee04f Foo App +a855ac937f2ed375ba4396bbc49c4093e124da933acd2713fb9bc17d7562a087 a855ac937f2e Quiet App +`, + options: imageListOption{}, }, { - Version: "1.0.0", - SchemaVersion: "1.0.0", - Name: "Foo App", + name: "TestListWithDigests", + expectedOutput: `REPOSITORY TAG DIGEST APP IMAGE ID APP NAME +foo/bar sha256:b59492bb814012ca3d2ce0b6728242d96b4af41687cc82166a4b5d7f2d9fb865 3f825b2d0657 Digested App +foo/bar 1.0 9aae408ee04f Foo App +a855ac937f2ed375ba4396bbc49c4093e124da933acd2713fb9bc17d7562a087 sha256:a855ac937f2ed375ba4396bbc49c4093e124da933acd2713fb9bc17d7562a087 a855ac937f2e Quiet App +`, + options: imageListOption{digests: true}, + }, + { + name: "TestListWithQuiet", + expectedOutput: `3f825b2d0657 +9aae408ee04f +a855ac937f2e +`, + options: imageListOption{quiet: true}, }, } - expectedOutput := `APP IMAGE DIGEST APP NAME -foo/bar@sha256:b59492bb814012ca3d2ce0b6728242d96b4af41687cc82166a4b5d7f2d9fb865 sha256:b59492bb814012ca3d2ce0b6728242d96b4af41687cc82166a4b5d7f2d9fb865 Digested App -foo/bar:1.0 Foo App -` - testRunList(t, refs, bundles, imageListOption{digests: true}, expectedOutput) + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + testRunList(t, refs, bundles, tc.options, tc.expectedOutput) + }) + } } func parseReference(t *testing.T, s string) reference.Reference {