diff --git a/cli/command/container/stats.go b/cli/command/container/stats.go index cd5f9038ac03..99d3fda6cd2c 100644 --- a/cli/command/container/stats.go +++ b/cli/command/container/stats.go @@ -13,33 +13,41 @@ import ( "github.com/docker/cli/cli/command/completion" "github.com/docker/cli/cli/command/formatter" flagsHelper "github.com/docker/cli/cli/flags" + "github.com/docker/cli/opts" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/events" - "github.com/docker/docker/api/types/filters" "github.com/pkg/errors" "github.com/spf13/cobra" ) -type statsOptions struct { - all bool - noStream bool - noTrunc bool - format string - containers []string +// StatsOptions defines options for the `stats` command +type StatsOptions struct { + All bool + NoStream bool + NoTrunc bool + Format string + Containers []string + Filter opts.FilterOpt +} + +func NewStatsOptions() StatsOptions { + return StatsOptions{ + Filter: opts.NewFilterOpt(), + } } // NewStatsCommand creates a new cobra.Command for `docker stats` func NewStatsCommand(dockerCli command.Cli) *cobra.Command { - var opts statsOptions + options := NewStatsOptions() cmd := &cobra.Command{ Use: "stats [OPTIONS] [CONTAINER...]", Short: "Display a live stream of container(s) resource usage statistics", Args: cli.RequiresMinArgs(0), RunE: func(cmd *cobra.Command, args []string) error { - opts.containers = args - return runStats(dockerCli, &opts) + options.Containers = args + return RunStats(dockerCli, &options) }, Annotations: map[string]string{ "aliases": "docker container stats, docker stats", @@ -48,19 +56,20 @@ func NewStatsCommand(dockerCli command.Cli) *cobra.Command { } flags := cmd.Flags() - flags.BoolVarP(&opts.all, "all", "a", false, "Show all containers (default shows just running)") - flags.BoolVar(&opts.noStream, "no-stream", false, "Disable streaming stats and only pull the first result") - flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Do not truncate output") - flags.StringVar(&opts.format, "format", "", flagsHelper.FormatHelp) + flags.BoolVarP(&options.All, "all", "a", false, "Show all containers (default shows just running)") + flags.BoolVar(&options.NoStream, "no-stream", false, "Disable streaming stats and only pull the first result") + flags.BoolVar(&options.NoTrunc, "no-trunc", false, "Do not truncate output") + flags.StringVar(&options.Format, "format", "", flagsHelper.FormatHelp) + flags.Var(&options.Filter, "filter", `Filter containers based on conditions provided`) return cmd } -// runStats displays a live stream of resource usage statistics for one or more containers. +// RunStats displays a live stream of resource usage statistics for one or more containers. // This shows real-time information on CPU usage, memory usage, and network I/O. // //nolint:gocyclo -func runStats(dockerCli command.Cli, opts *statsOptions) error { - showAll := len(opts.containers) == 0 +func RunStats(dockerCli command.Cli, opts *StatsOptions) error { + showAll := len(opts.Containers) == 0 closeChan := make(chan error) ctx := context.Background() @@ -68,7 +77,7 @@ func runStats(dockerCli command.Cli, opts *statsOptions) error { // monitorContainerEvents watches for container creation and removal (only // used when calling `docker stats` without arguments). monitorContainerEvents := func(started chan<- struct{}, c chan events.Message, stopped <-chan struct{}) { - f := filters.NewArgs() + f := opts.Filter.Value() f.Add("type", "container") options := types.EventsOptions{ Filters: f, @@ -112,7 +121,7 @@ func runStats(dockerCli command.Cli, opts *statsOptions) error { // containers (only used when calling `docker stats` without arguments). getContainerList := func() { options := container.ListOptions{ - All: opts.all, + All: opts.All, } cs, err := dockerCli.Client().ContainerList(ctx, options) if err != nil { @@ -122,7 +131,7 @@ func runStats(dockerCli command.Cli, opts *statsOptions) error { s := NewStats(ctr.ID[:12]) if cStats.add(s) { waitFirst.Add(1) - go collect(ctx, s, dockerCli.Client(), !opts.noStream, waitFirst) + go collect(ctx, s, dockerCli.Client(), !opts.NoStream, waitFirst) } } } @@ -135,11 +144,11 @@ func runStats(dockerCli command.Cli, opts *statsOptions) error { started := make(chan struct{}) eh := command.InitEventHandler() eh.Handle(events.ActionCreate, func(e events.Message) { - if opts.all { + if opts.All { s := NewStats(e.Actor.ID[:12]) if cStats.add(s) { waitFirst.Add(1) - go collect(ctx, s, dockerCli.Client(), !opts.noStream, waitFirst) + go collect(ctx, s, dockerCli.Client(), !opts.NoStream, waitFirst) } } }) @@ -148,12 +157,12 @@ func runStats(dockerCli command.Cli, opts *statsOptions) error { s := NewStats(e.Actor.ID[:12]) if cStats.add(s) { waitFirst.Add(1) - go collect(ctx, s, dockerCli.Client(), !opts.noStream, waitFirst) + go collect(ctx, s, dockerCli.Client(), !opts.NoStream, waitFirst) } }) eh.Handle(events.ActionDie, func(e events.Message) { - if !opts.all { + if !opts.All { cStats.remove(e.Actor.ID[:12]) } }) @@ -174,11 +183,11 @@ func runStats(dockerCli command.Cli, opts *statsOptions) error { } else { // Artificially send creation events for the containers we were asked to // monitor (same code path than we use when monitoring all containers). - for _, name := range opts.containers { + for _, name := range opts.Containers { s := NewStats(name) if cStats.add(s) { waitFirst.Add(1) - go collect(ctx, s, dockerCli.Client(), !opts.noStream, waitFirst) + go collect(ctx, s, dockerCli.Client(), !opts.NoStream, waitFirst) } } @@ -201,7 +210,7 @@ func runStats(dockerCli command.Cli, opts *statsOptions) error { } } - format := opts.format + format := opts.Format if len(format) == 0 { if len(dockerCli.ConfigFile().StatsFormat) > 0 { format = dockerCli.ConfigFile().StatsFormat @@ -214,7 +223,7 @@ func runStats(dockerCli command.Cli, opts *statsOptions) error { Format: NewStatsFormat(format, daemonOSType), } cleanScreen := func() { - if !opts.noStream { + if !opts.NoStream { fmt.Fprint(dockerCli.Out(), "\033[2J") fmt.Fprint(dockerCli.Out(), "\033[H") } @@ -231,13 +240,13 @@ func runStats(dockerCli command.Cli, opts *statsOptions) error { ccstats = append(ccstats, c.GetStatistics()) } cStats.mu.RUnlock() - if err = statsFormatWrite(statsCtx, ccstats, daemonOSType, !opts.noTrunc); err != nil { + if err = statsFormatWrite(statsCtx, ccstats, daemonOSType, !opts.NoTrunc); err != nil { break } if len(cStats.cs) == 0 && !showAll { break } - if opts.noStream { + if opts.NoStream { break } select {