@@ -22,6 +22,7 @@ import (
2222 "fmt"
2323 "sort"
2424 "strings"
25+ "sync"
2526 "time"
2627
2728 containerd "github.com/containerd/containerd/v2/client"
@@ -40,11 +41,11 @@ import (
4041
4142// List prints containers according to `options`.
4243func 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 )
4445 if err != nil {
4546 return nil , err
4647 }
47- return prepareContainers (ctx , client , containers , options )
48+ return prepareContainers (ctx , client , containers , cMap , options )
4849}
4950
5051// filterContainers returns containers matching the filters.
@@ -53,14 +54,14 @@ func List(ctx context.Context, client *containerd.Client, options types.Containe
5354// - all means showing all containers (default shows just running).
5455// - lastN means only showing n last created containers (includes all states). Non-positive values are ignored.
5556// 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 ) {
5758 containers , err := client .Containers (ctx )
5859 if err != nil {
59- return nil , err
60+ return nil , nil , err
6061 }
6162 filterCtx , err := foldContainerFilters (ctx , containers , filters )
6263 if err != nil {
63- return nil , err
64+ return nil , nil , err
6465 }
6566 containers = filterCtx .MatchesFilters (ctx )
6667
@@ -77,18 +78,37 @@ func filterContainers(ctx context.Context, client *containerd.Client, filters []
7778 }
7879 }
7980
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 ()
80100 if all || filterCtx .all {
81- return containers , nil
101+ return containers , statusPerContainer , nil
82102 }
83103
84104 var upContainers []containerd.Container
85105 for _ , c := range containers {
86- cStatus := formatter . ContainerStatus ( ctx , c )
106+ cStatus := statusPerContainer [ c . ID ()]
87107 if strings .HasPrefix (cStatus , "Up" ) {
88108 upContainers = append (upContainers , c )
89109 }
90110 }
91- return upContainers , nil
111+ return upContainers , statusPerContainer , nil
92112}
93113
94114type ListItem struct {
@@ -112,7 +132,7 @@ func (x *ListItem) Label(s string) string {
112132 return x .LabelsMap [s ]
113133}
114134
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 ) {
116136 listItems := make ([]ListItem , len (containers ))
117137 snapshottersCache := map [string ]snapshots.Snapshotter {}
118138 for i , c := range containers {
@@ -136,6 +156,12 @@ func prepareContainers(ctx context.Context, client *containerd.Client, container
136156 if options .Truncate && len (id ) > 12 {
137157 id = id [:12 ]
138158 }
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+ }
139165 li := ListItem {
140166 Command : formatter .InspectContainerCommand (spec , options .Truncate , true ),
141167 CreatedAt : info .CreatedAt ,
@@ -144,7 +170,7 @@ func prepareContainers(ctx context.Context, client *containerd.Client, container
144170 Platform : info .Labels [labels .Platform ],
145171 Names : containerutil .GetContainerName (info .Labels ),
146172 Ports : formatter .FormatPorts (info .Labels ),
147- Status : formatter . ContainerStatus ( ctx , c ) ,
173+ Status : status ,
148174 Runtime : info .Runtime .Name ,
149175 Labels : formatter .FormatLabels (info .Labels ),
150176 LabelsMap : info .Labels ,
0 commit comments