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 {