@@ -2,12 +2,16 @@ package image
22
33import (
44 "bytes"
5+ "encoding/json"
56 "fmt"
67 "io"
78 "strings"
89 "text/tabwriter"
910 "time"
1011
12+ "github.com/docker/cli/templates"
13+ "github.com/pkg/errors"
14+
1115 "github.com/docker/app/internal/packager"
1216 "github.com/docker/app/internal/relocated"
1317 "github.com/docker/app/internal/store"
@@ -20,13 +24,14 @@ import (
2024)
2125
2226type imageListOption struct {
23- quiet bool
24- digests bool
27+ quiet bool
28+ digests bool
29+ template string
2530}
2631
2732type imageListColumn struct {
2833 header string
29- value func (p pkg ) string
34+ value func (desc imageDesc ) string
3035}
3136
3237func listCmd (dockerCli command.Cli ) * cobra.Command {
@@ -52,6 +57,8 @@ func listCmd(dockerCli command.Cli) *cobra.Command {
5257 flags := cmd .Flags ()
5358 flags .BoolVarP (& options .quiet , "quiet" , "q" , false , "Only show numeric IDs" )
5459 flags .BoolVarP (& options .digests , "digests" , "" , false , "Show image digests" )
60+ cmd .Flags ().StringVarP (& options .template , "format" , "f" , "" , "Format the output using the given syntax or Go template" )
61+ cmd .Flags ().SetAnnotation ("format" , "experimentalCLI" , []string {"true" }) //nolint:errcheck
5562
5663 return cmd
5764}
@@ -94,10 +101,32 @@ func getPackages(bundleStore store.BundleStore, references []reference.Reference
94101
95102func printImages (dockerCli command.Cli , refs []pkg , options imageListOption ) error {
96103 w := tabwriter .NewWriter (dockerCli .Out (), 0 , 0 , 1 , ' ' , 0 )
104+
105+ list := []imageDesc {}
106+ for _ , ref := range refs {
107+ list = append (list , getImageDesc (ref ))
108+ }
109+
110+ if options .template == "json" {
111+ bytes , err := json .MarshalIndent (list , "" , " " )
112+ if err != nil {
113+ return errors .Errorf ("Failed to marshall json: %s" , err )
114+ }
115+ _ , err = dockerCli .Out ().Write (bytes )
116+ return err
117+ }
118+ if options .template != "" {
119+ tmpl , err := templates .Parse (options .template )
120+ if err != nil {
121+ return errors .Errorf ("Template parsing error: %s" , err )
122+ }
123+ return tmpl .Execute (dockerCli .Out (), list )
124+ }
125+
97126 listColumns := getImageListColumns (options )
98127 printHeaders (w , listColumns )
99- for _ , ref := range refs {
100- printValues (w , ref , listColumns )
128+ for _ , desc := range list {
129+ printValues (w , desc , listColumns )
101130 }
102131
103132 return w .Flush ()
@@ -137,55 +166,87 @@ func printHeaders(w io.Writer, listColumns []imageListColumn) {
137166 fmt .Fprintln (w , strings .Join (headers , "\t " ))
138167}
139168
140- func printValues (w io.Writer , ref pkg , listColumns []imageListColumn ) {
169+ func printValues (w io.Writer , desc imageDesc , listColumns []imageListColumn ) {
141170 var values []string
142171 for _ , column := range listColumns {
143- values = append (values , column .value (ref ))
172+ values = append (values , column .value (desc ))
144173 }
145174 fmt .Fprintln (w , strings .Join (values , "\t " ))
146175}
147176
177+ type imageDesc struct {
178+ ID string `json:"id,omitempty"`
179+ Name string `json:"name,omitempty"`
180+ Repository string `json:"repository,omitempty"`
181+ Tag string `json:"tag,omitempty"`
182+ Digest string `json:"digest,omitempty"`
183+ Created time.Duration `json:"created,omitempty"`
184+ }
185+
186+ func getImageDesc (p pkg ) imageDesc {
187+ var id string
188+ id , _ = getImageID (p )
189+ var repository string
190+ if n , ok := p .ref .(reference.Named ); ok {
191+ repository = reference .FamiliarName (n )
192+ }
193+ var tag string
194+ if t , ok := p .ref .(reference.Tagged ); ok {
195+ tag = t .Tag ()
196+ }
197+ var digest string
198+ if t , ok := p .ref .(reference.Digested ); ok {
199+ digest = t .Digest ().String ()
200+ }
201+ var created time.Duration
202+ if payload , err := packager .CustomPayload (p .bundle .Bundle ); err == nil {
203+ if createdPayload , ok := payload .(packager.CustomPayloadCreated ); ok {
204+ created = time .Now ().UTC ().Sub (createdPayload .CreatedTime ())
205+ }
206+ }
207+ return imageDesc {
208+ ID : id ,
209+ Name : p .bundle .Name ,
210+ Repository : repository ,
211+ Tag : tag ,
212+ Digest : digest ,
213+ Created : created ,
214+ }
215+ }
216+
148217func getImageListColumns (options imageListOption ) []imageListColumn {
149218 columns := []imageListColumn {
150- {"REPOSITORY" , func (p pkg ) string {
151- if n , ok := p . ref .(reference. Named ); ok {
152- return reference . FamiliarName ( n )
219+ {"REPOSITORY" , func (desc imageDesc ) string {
220+ if desc . Repository != "" {
221+ return desc . Repository
153222 }
154223 return "<none>"
155224 }},
156- {"TAG" , func (p pkg ) string {
157- if t , ok := p . ref .(reference. Tagged ); ok {
158- return t .Tag ()
225+ {"TAG" , func (desc imageDesc ) string {
226+ if desc . Tag != "" {
227+ return desc .Tag
159228 }
160229 return "<none>"
161230 }},
162231 }
163232 if options .digests {
164- columns = append (columns , imageListColumn {"DIGEST" , func (p pkg ) string {
165- if t , ok := p . ref .(reference. Digested ); ok {
166- return t .Digest (). String ()
233+ columns = append (columns , imageListColumn {"DIGEST" , func (desc imageDesc ) string {
234+ if desc . Digest != "" {
235+ return desc .Digest
167236 }
168237 return "<none>"
169238 }})
170239 }
171240 columns = append (columns ,
172- imageListColumn {"APP IMAGE ID" , func (p pkg ) string {
173- id , err := getImageID (p )
174- if err != nil {
175- return ""
176- }
177- return id
241+ imageListColumn {"APP IMAGE ID" , func (desc imageDesc ) string {
242+ return desc .ID
178243 }},
179- imageListColumn {"APP NAME" , func (p pkg ) string {
180- return p . bundle .Name
244+ imageListColumn {"APP NAME" , func (desc imageDesc ) string {
245+ return desc .Name
181246 }},
182- imageListColumn {"CREATED" , func (p pkg ) string {
183- payload , err := packager .CustomPayload (p .bundle .Bundle )
184- if err != nil {
185- return ""
186- }
187- if createdPayload , ok := payload .(packager.CustomPayloadCreated ); ok {
188- return units .HumanDuration (time .Now ().UTC ().Sub (createdPayload .CreatedTime ())) + " ago"
247+ imageListColumn {"CREATED" , func (desc imageDesc ) string {
248+ if desc .Created > 0 {
249+ return units .HumanDuration (desc .Created ) + " ago"
189250 }
190251 return ""
191252 }},
0 commit comments