@@ -10,13 +10,13 @@ import (
1010 "sync"
1111 "time"
1212
13+ "github.com/containerd/errdefs"
1314 "github.com/docker/cli/cli"
1415 "github.com/docker/cli/cli/command"
1516 "github.com/docker/cli/cli/command/completion"
1617 "github.com/docker/cli/cli/command/formatter"
1718 flagsHelper "github.com/docker/cli/cli/flags"
1819 "github.com/moby/moby/api/types/events"
19- "github.com/moby/moby/api/types/filters"
2020 "github.com/moby/moby/client"
2121 "github.com/sirupsen/logrus"
2222 "github.com/spf13/cobra"
@@ -60,7 +60,7 @@ type StatsOptions struct {
6060 // filter options may be added in future (within the constraints described
6161 // above), but may require daemon-side validation as the list of accepted
6262 // filters can differ between daemon- and API versions.
63- Filters * filters. Args
63+ Filters client. Filters
6464}
6565
6666// newStatsCommand creates a new [cobra.Command] for "docker container stats".
@@ -101,6 +101,24 @@ var acceptedStatsFilters = map[string]bool{
101101 "label" : true ,
102102}
103103
104+ // cloneFilters returns a deep copy of f.
105+ //
106+ // TODO(thaJeztah): add this as a "Clone" method on client.Filters.
107+ func cloneFilters (f client.Filters ) client.Filters {
108+ if f == nil {
109+ return nil
110+ }
111+ out := make (client.Filters , len (f ))
112+ for term , values := range f {
113+ inner := make (map [string ]bool , len (values ))
114+ for v , ok := range values {
115+ inner [v ] = ok
116+ }
117+ out [term ] = inner
118+ }
119+ return out
120+ }
121+
104122// RunStats displays a live stream of resource usage statistics for one or more containers.
105123// This shows real-time information on CPU usage, memory usage, and network I/O.
106124//
@@ -123,12 +141,14 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
123141 started := make (chan struct {})
124142
125143 if options .Filters == nil {
126- f := filters .NewArgs ()
127- options .Filters = & f
144+ options .Filters = make (client.Filters )
128145 }
129146
130- if err := options .Filters .Validate (acceptedStatsFilters ); err != nil {
131- return err
147+ // FIXME(thaJeztah): any way we can (and should?) validate allowed filters?
148+ for filter := range options .Filters {
149+ if _ , ok := acceptedStatsFilters [filter ]; ! ok {
150+ return errdefs .ErrInvalidArgument .WithMessage ("invalid filter '" + filter + "'" )
151+ }
132152 }
133153
134154 eh := newEventHandler ()
@@ -163,7 +183,7 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
163183 // the original set of filters. Custom filters are used both
164184 // to list containers and to filter events, but the "type" filter
165185 // is not valid for filtering containers.
166- f := options .Filters . Clone ( )
186+ f := cloneFilters ( options .Filters )
167187 f .Add ("type" , string (events .ContainerEventType ))
168188 eventChan , errChan := apiClient .Events (ctx , client.EventsListOptions {
169189 Filters : f ,
@@ -199,7 +219,7 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
199219 // to refresh the list of containers.
200220 cs , err := apiClient .ContainerList (ctx , client.ContainerListOptions {
201221 All : options .All ,
202- Filters : * options .Filters ,
222+ Filters : options .Filters ,
203223 })
204224 if err != nil {
205225 return err
@@ -219,7 +239,7 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
219239 // only a single code-path is needed, and custom filters can be combined
220240 // with a list of container names/IDs.
221241
222- if options .Filters != nil && options . Filters . Len ( ) > 0 {
242+ if len ( options .Filters ) > 0 {
223243 return errors .New ("filtering is not supported when specifying a list of containers" )
224244 }
225245
0 commit comments