@@ -2,12 +2,16 @@ package image
2
2
3
3
import (
4
4
"bytes"
5
+ "encoding/json"
5
6
"fmt"
6
7
"io"
7
8
"strings"
8
9
"text/tabwriter"
9
10
"time"
10
11
12
+ "github.com/docker/cli/templates"
13
+ "github.com/pkg/errors"
14
+
11
15
"github.com/docker/app/internal/packager"
12
16
"github.com/docker/app/internal/relocated"
13
17
"github.com/docker/app/internal/store"
@@ -20,13 +24,14 @@ import (
20
24
)
21
25
22
26
type imageListOption struct {
23
- quiet bool
24
- digests bool
27
+ quiet bool
28
+ digests bool
29
+ template string
25
30
}
26
31
27
32
type imageListColumn struct {
28
33
header string
29
- value func (p pkg ) string
34
+ value func (desc imageDesc ) string
30
35
}
31
36
32
37
func listCmd (dockerCli command.Cli ) * cobra.Command {
@@ -52,6 +57,8 @@ func listCmd(dockerCli command.Cli) *cobra.Command {
52
57
flags := cmd .Flags ()
53
58
flags .BoolVarP (& options .quiet , "quiet" , "q" , false , "Only show numeric IDs" )
54
59
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
55
62
56
63
return cmd
57
64
}
@@ -94,10 +101,32 @@ func getPackages(bundleStore store.BundleStore, references []reference.Reference
94
101
95
102
func printImages (dockerCli command.Cli , refs []pkg , options imageListOption ) error {
96
103
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
+
97
126
listColumns := getImageListColumns (options )
98
127
printHeaders (w , listColumns )
99
- for _ , ref := range refs {
100
- printValues (w , ref , listColumns )
128
+ for _ , desc := range list {
129
+ printValues (w , desc , listColumns )
101
130
}
102
131
103
132
return w .Flush ()
@@ -137,55 +166,87 @@ func printHeaders(w io.Writer, listColumns []imageListColumn) {
137
166
fmt .Fprintln (w , strings .Join (headers , "\t " ))
138
167
}
139
168
140
- func printValues (w io.Writer , ref pkg , listColumns []imageListColumn ) {
169
+ func printValues (w io.Writer , desc imageDesc , listColumns []imageListColumn ) {
141
170
var values []string
142
171
for _ , column := range listColumns {
143
- values = append (values , column .value (ref ))
172
+ values = append (values , column .value (desc ))
144
173
}
145
174
fmt .Fprintln (w , strings .Join (values , "\t " ))
146
175
}
147
176
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
+
148
217
func getImageListColumns (options imageListOption ) []imageListColumn {
149
218
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
153
222
}
154
223
return "<none>"
155
224
}},
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
159
228
}
160
229
return "<none>"
161
230
}},
162
231
}
163
232
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
167
236
}
168
237
return "<none>"
169
238
}})
170
239
}
171
240
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
178
243
}},
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
181
246
}},
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"
189
250
}
190
251
return ""
191
252
}},
0 commit comments