@@ -22,6 +22,7 @@ import (
22
22
"fmt"
23
23
"sort"
24
24
"strings"
25
+ "sync"
25
26
"time"
26
27
27
28
containerd "github.com/containerd/containerd/v2/client"
@@ -40,11 +41,11 @@ import (
40
41
41
42
// List prints containers according to `options`.
42
43
func List (ctx context.Context , client * containerd.Client , options types.ContainerListOptions ) ([]ListItem , error ) {
43
- containers , err := filterContainers (ctx , client , options .Filters , options .LastN , options .All )
44
+ containers , cMap , err := filterContainers (ctx , client , options .Filters , options .LastN , options .All )
44
45
if err != nil {
45
46
return nil , err
46
47
}
47
- return prepareContainers (ctx , client , containers , options )
48
+ return prepareContainers (ctx , client , containers , cMap , options )
48
49
}
49
50
50
51
// filterContainers returns containers matching the filters.
@@ -53,14 +54,14 @@ func List(ctx context.Context, client *containerd.Client, options types.Containe
53
54
// - all means showing all containers (default shows just running).
54
55
// - lastN means only showing n last created containers (includes all states). Non-positive values are ignored.
55
56
// In other words, if lastN is positive, all will be set to true.
56
- func filterContainers (ctx context.Context , client * containerd.Client , filters []string , lastN int , all bool ) ([]containerd.Container , error ) {
57
+ func filterContainers (ctx context.Context , client * containerd.Client , filters []string , lastN int , all bool ) ([]containerd.Container , map [ string ] string , error ) {
57
58
containers , err := client .Containers (ctx )
58
59
if err != nil {
59
- return nil , err
60
+ return nil , nil , err
60
61
}
61
62
filterCtx , err := foldContainerFilters (ctx , containers , filters )
62
63
if err != nil {
63
- return nil , err
64
+ return nil , nil , err
64
65
}
65
66
containers = filterCtx .MatchesFilters (ctx )
66
67
@@ -77,18 +78,37 @@ func filterContainers(ctx context.Context, client *containerd.Client, filters []
77
78
}
78
79
}
79
80
81
+ var wg sync.WaitGroup
82
+ statusPerContainer := make (map [string ]string )
83
+ var mu sync.Mutex
84
+ // formatter.ContainerStatus(ctx, c) is time consuming so we do it in goroutines and return the container's id with status as a map.
85
+ // prepareContainers func will use this map to avoid call formatter.ContainerStatus again.
86
+ for _ , c := range containers {
87
+ if c .ID () == "" {
88
+ return nil , nil , fmt .Errorf ("container id is nill" )
89
+ }
90
+ wg .Add (1 )
91
+ go func (ctx context.Context , c containerd.Container ) {
92
+ defer wg .Done ()
93
+ cStatus := formatter .ContainerStatus (ctx , c )
94
+ mu .Lock ()
95
+ statusPerContainer [c .ID ()] = cStatus
96
+ mu .Unlock ()
97
+ }(ctx , c )
98
+ }
99
+ wg .Wait ()
80
100
if all || filterCtx .all {
81
- return containers , nil
101
+ return containers , statusPerContainer , nil
82
102
}
83
103
84
104
var upContainers []containerd.Container
85
105
for _ , c := range containers {
86
- cStatus := formatter . ContainerStatus ( ctx , c )
106
+ cStatus := statusPerContainer [ c . ID ()]
87
107
if strings .HasPrefix (cStatus , "Up" ) {
88
108
upContainers = append (upContainers , c )
89
109
}
90
110
}
91
- return upContainers , nil
111
+ return upContainers , statusPerContainer , nil
92
112
}
93
113
94
114
type ListItem struct {
@@ -112,7 +132,7 @@ func (x *ListItem) Label(s string) string {
112
132
return x .LabelsMap [s ]
113
133
}
114
134
115
- func prepareContainers (ctx context.Context , client * containerd.Client , containers []containerd.Container , options types.ContainerListOptions ) ([]ListItem , error ) {
135
+ func prepareContainers (ctx context.Context , client * containerd.Client , containers []containerd.Container , statusPerContainer map [ string ] string , options types.ContainerListOptions ) ([]ListItem , error ) {
116
136
listItems := make ([]ListItem , len (containers ))
117
137
snapshottersCache := map [string ]snapshots.Snapshotter {}
118
138
for i , c := range containers {
@@ -136,6 +156,12 @@ func prepareContainers(ctx context.Context, client *containerd.Client, container
136
156
if options .Truncate && len (id ) > 12 {
137
157
id = id [:12 ]
138
158
}
159
+ var status string
160
+ if s , ok := statusPerContainer [c .ID ()]; ok {
161
+ status = s
162
+ } else {
163
+ return nil , fmt .Errorf ("can't get container %s status" , c .ID ())
164
+ }
139
165
li := ListItem {
140
166
Command : formatter .InspectContainerCommand (spec , options .Truncate , true ),
141
167
CreatedAt : info .CreatedAt ,
@@ -144,7 +170,7 @@ func prepareContainers(ctx context.Context, client *containerd.Client, container
144
170
Platform : info .Labels [labels .Platform ],
145
171
Names : containerutil .GetContainerName (info .Labels ),
146
172
Ports : formatter .FormatPorts (info .Labels ),
147
- Status : formatter . ContainerStatus ( ctx , c ) ,
173
+ Status : status ,
148
174
Runtime : info .Runtime .Name ,
149
175
Labels : formatter .FormatLabels (info .Labels ),
150
176
LabelsMap : info .Labels ,
0 commit comments