Skip to content
This repository was archived by the owner on Jul 18, 2025. It is now read-only.

Commit e4b9653

Browse files
authored
Merge pull request #695 from jcsirot/add-quiet-flag-image-ls
Add -q flag for image ls command
2 parents f611ee7 + fb9defa commit e4b9653

File tree

4 files changed

+146
-2
lines changed

4 files changed

+146
-2
lines changed

e2e/images_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package e2e
22

33
import (
4+
"bufio"
45
"fmt"
56
"path/filepath"
7+
"strings"
68
"testing"
79

810
"gotest.tools/assert"
@@ -26,6 +28,23 @@ func expectImageListOutput(t *testing.T, cmd icmd.Cmd, output string) {
2628
assert.Equal(t, result.Stdout(), output)
2729
}
2830

31+
func verifyImageIDListOutput(t *testing.T, cmd icmd.Cmd, count int, distinct int) {
32+
cmd.Command = dockerCli.Command("app", "image", "ls", "-q")
33+
result := icmd.RunCmd(cmd).Assert(t, icmd.Success)
34+
scanner := bufio.NewScanner(strings.NewReader(result.Stdout()))
35+
lines := []string{}
36+
counter := make(map[string]int)
37+
for scanner.Scan() {
38+
lines = append(lines, scanner.Text())
39+
counter[scanner.Text()]++
40+
}
41+
if err := scanner.Err(); err != nil {
42+
assert.Error(t, err, "Verification failed")
43+
}
44+
assert.Equal(t, len(lines), count)
45+
assert.Equal(t, len(counter), distinct)
46+
}
47+
2948
func TestImageList(t *testing.T) {
3049
runWithDindSwarmAndRegistry(t, func(info dindSwarmAndRegistryInfo) {
3150
cmd := info.configuredCmd
@@ -44,6 +63,16 @@ b-simple-app:latest simple
4463
})
4564
}
4665

66+
func TestImageListQuiet(t *testing.T) {
67+
runWithDindSwarmAndRegistry(t, func(info dindSwarmAndRegistryInfo) {
68+
cmd := info.configuredCmd
69+
dir := fs.NewDir(t, "")
70+
defer dir.Remove()
71+
insertBundles(t, cmd, info)
72+
verifyImageIDListOutput(t, cmd, 3, 2)
73+
})
74+
}
75+
4776
func TestImageRm(t *testing.T) {
4877
runWithDindSwarmAndRegistry(t, func(info dindSwarmAndRegistryInfo) {
4978
cmd := info.configuredCmd

internal/commands/image/list.go

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package image
22

33
import (
4+
"bytes"
45
"fmt"
56
"io"
67
"strings"
@@ -11,10 +12,16 @@ import (
1112
"github.com/docker/cli/cli/command"
1213
"github.com/docker/cli/cli/config"
1314
"github.com/docker/distribution/reference"
15+
"github.com/docker/docker/pkg/stringid"
1416
"github.com/spf13/cobra"
1517
)
1618

19+
type imageListOption struct {
20+
quiet bool
21+
}
22+
1723
func listCmd(dockerCli command.Cli) *cobra.Command {
24+
options := imageListOption{}
1825
cmd := &cobra.Command{
1926
Short: "List application images",
2027
Use: "ls",
@@ -30,14 +37,16 @@ func listCmd(dockerCli command.Cli) *cobra.Command {
3037
return err
3138
}
3239

33-
return runList(dockerCli, bundleStore)
40+
return runList(dockerCli, options, bundleStore)
3441
},
3542
}
43+
flags := cmd.Flags()
44+
flags.BoolVarP(&options.quiet, "quiet", "q", false, "Only show numeric IDs")
3645

3746
return cmd
3847
}
3948

40-
func runList(dockerCli command.Cli, bundleStore store.BundleStore) error {
49+
func runList(dockerCli command.Cli, options imageListOption, bundleStore store.BundleStore) error {
4150
bundles, err := bundleStore.List()
4251
if err != nil {
4352
return err
@@ -48,6 +57,9 @@ func runList(dockerCli command.Cli, bundleStore store.BundleStore) error {
4857
return err
4958
}
5059

60+
if options.quiet {
61+
return printImageIDs(dockerCli, pkgs)
62+
}
5163
return printImages(dockerCli, pkgs)
5264
}
5365

@@ -81,6 +93,24 @@ func printImages(dockerCli command.Cli, refs []pkg) error {
8193
return w.Flush()
8294
}
8395

96+
func printImageIDs(dockerCli command.Cli, refs []pkg) error {
97+
var buf bytes.Buffer
98+
99+
for _, ref := range refs {
100+
id, ok := ref.ref.(store.ID)
101+
if !ok {
102+
var err error
103+
id, err = store.FromBundle(ref.bundle)
104+
if err != nil {
105+
return err
106+
}
107+
}
108+
fmt.Fprintln(&buf, stringid.TruncateID(id.String()))
109+
}
110+
fmt.Fprintf(dockerCli.Out(), buf.String())
111+
return nil
112+
}
113+
84114
func printHeaders(w io.Writer) {
85115
var headers []string
86116
for _, column := range listColumns {

internal/commands/image/list_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package image
2+
3+
import (
4+
"bufio"
5+
"bytes"
6+
"fmt"
7+
"testing"
8+
9+
"gotest.tools/assert"
10+
11+
"github.com/deislabs/cnab-go/bundle"
12+
"github.com/docker/app/internal/store"
13+
"github.com/docker/cli/cli/command"
14+
"github.com/docker/distribution/reference"
15+
)
16+
17+
type mockRef string
18+
19+
func (ref mockRef) String() string {
20+
return string(ref)
21+
}
22+
23+
type bundleStoreStubForListCmd struct {
24+
refMap map[reference.Reference]*bundle.Bundle
25+
// in order to keep the reference in the same order between tests
26+
refList []reference.Reference
27+
}
28+
29+
func (b *bundleStoreStubForListCmd) Store(ref reference.Reference, bndle *bundle.Bundle) (reference.Reference, error) {
30+
b.refMap[ref] = bndle
31+
b.refList = append(b.refList, ref)
32+
return ref, nil
33+
}
34+
35+
func (b *bundleStoreStubForListCmd) Read(ref reference.Reference) (*bundle.Bundle, error) {
36+
bndl, ok := b.refMap[ref]
37+
if ok {
38+
return bndl, nil
39+
}
40+
return nil, fmt.Errorf("Bundle not found")
41+
}
42+
43+
func (b *bundleStoreStubForListCmd) List() ([]reference.Reference, error) {
44+
return b.refList, nil
45+
}
46+
47+
func (b *bundleStoreStubForListCmd) Remove(ref reference.Reference) error {
48+
return nil
49+
}
50+
51+
func TestListWithQuietFlag(t *testing.T) {
52+
var buf bytes.Buffer
53+
w := bufio.NewWriter(&buf)
54+
dockerCli, err := command.NewDockerCli(command.WithOutputStream(w))
55+
assert.NilError(t, err)
56+
bundleStore := &bundleStoreStubForListCmd{
57+
refMap: make(map[reference.Reference]*bundle.Bundle),
58+
refList: []reference.Reference{},
59+
}
60+
ref1, err := store.FromString("a855ac937f2ed375ba4396bbc49c4093e124da933acd2713fb9bc17d7562a087")
61+
assert.NilError(t, err)
62+
ref2, err := reference.Parse("foo/bar:1.0")
63+
assert.NilError(t, err)
64+
_, err = bundleStore.Store(ref1, &bundle.Bundle{})
65+
assert.NilError(t, err)
66+
_, err = bundleStore.Store(ref2, &bundle.Bundle{
67+
Version: "1.0.0",
68+
SchemaVersion: "1.0.0",
69+
Name: "Foo App",
70+
})
71+
assert.NilError(t, err)
72+
err = runList(dockerCli, imageListOption{quiet: true}, bundleStore)
73+
assert.NilError(t, err)
74+
expectedOutput := `a855ac937f2e
75+
9aae408ee04f
76+
`
77+
w.Flush()
78+
assert.Equal(t, buf.String(), expectedOutput)
79+
}

internal/store/digest.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"io"
77
"regexp"
88

9+
"github.com/deislabs/cnab-go/bundle"
910
"github.com/docker/distribution/reference"
1011
"github.com/opencontainers/go-digest"
1112
)
@@ -27,6 +28,11 @@ func FromString(s string) (ID, error) {
2728
return ID{s}, nil
2829
}
2930

31+
func FromBundle(bndle *bundle.Bundle) (ID, error) {
32+
digest, err := ComputeDigest(bndle)
33+
return ID{digest.Encoded()}, err
34+
}
35+
3036
// ID is an unique identifier for docker app image bundle, implementing reference.Reference
3137
type ID struct {
3238
digest string

0 commit comments

Comments
 (0)