diff --git a/cmd/podman/containers/stats.go b/cmd/podman/containers/stats.go index 79b549a1d8..576ecc2705 100644 --- a/cmd/podman/containers/stats.go +++ b/cmd/podman/containers/stats.go @@ -120,6 +120,7 @@ func stats(cmd *cobra.Command, args []string) error { Latest: statsOptions.Latest, Stream: !statsOptions.NoStream, Interval: statsOptions.Interval, + All: statsOptions.All, } args = putils.RemoveSlash(args) statsChan, err := registry.ContainerEngine().ContainerStats(registry.Context(), args, opts) diff --git a/pkg/api/handlers/libpod/containers_stats.go b/pkg/api/handlers/libpod/containers_stats.go index 687d61c12c..cc4c74644f 100644 --- a/pkg/api/handlers/libpod/containers_stats.go +++ b/pkg/api/handlers/libpod/containers_stats.go @@ -34,9 +34,11 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { Containers []string `schema:"containers"` Stream bool `schema:"stream"` Interval int `schema:"interval"` + All bool `schema:"all"` }{ Stream: true, Interval: 5, + All: false, } if err := decoder.Decode(&query, r.URL.Query()); err != nil { utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) @@ -50,6 +52,7 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { statsOptions := entities.ContainerStatsOptions{ Stream: query.Stream, Interval: query.Interval, + All: query.All, } // Stats will stop if the connection is closed. diff --git a/pkg/bindings/containers/types.go b/pkg/bindings/containers/types.go index eaab8b76ec..3682e950ee 100644 --- a/pkg/bindings/containers/types.go +++ b/pkg/bindings/containers/types.go @@ -208,6 +208,7 @@ type StartOptions struct { // //go:generate go run ../generator/generator.go StatsOptions type StatsOptions struct { + All *bool Stream *bool Interval *int } diff --git a/pkg/bindings/containers/types_stats_options.go b/pkg/bindings/containers/types_stats_options.go index 51b3fb41da..968f824d57 100644 --- a/pkg/bindings/containers/types_stats_options.go +++ b/pkg/bindings/containers/types_stats_options.go @@ -17,6 +17,21 @@ func (o *StatsOptions) ToParams() (url.Values, error) { return util.ToParams(o) } +// WithAll set field All to given value +func (o *StatsOptions) WithAll(value bool) *StatsOptions { + o.All = &value + return o +} + +// GetAll returns value of field All +func (o *StatsOptions) GetAll() bool { + if o.All == nil { + var z bool + return z + } + return *o.All +} + // WithStream set field Stream to given value func (o *StatsOptions) WithStream(value bool) *StatsOptions { o.Stream = &value diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index 596a444cbe..a47b9ed20c 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -472,6 +472,8 @@ type ContainerCpOptions struct { // ContainerStatsOptions describes input options for getting // stats on containers type ContainerStatsOptions struct { + // Get all containers stats + All bool // Operate on the latest known container. Only supported for local // clients. Latest bool diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 62102b45d8..01788368f6 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -1526,7 +1526,7 @@ func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []stri } statsChan = make(chan entities.ContainerStatsReport, 1) - containerFunc := ic.Libpod.GetRunningContainers + var containerFunc func() ([]*libpod.Container, error) queryAll := false switch { case options.Latest: @@ -1539,10 +1539,14 @@ func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []stri } case len(namesOrIds) > 0: containerFunc = func() ([]*libpod.Container, error) { return ic.Libpod.GetContainersByList(namesOrIds) } - default: - // No containers, no latest -> query all! + case options.All: queryAll = true containerFunc = ic.Libpod.GetAllContainers + default: + // queryAll is used to ignore errors when the container was removed between listing and + // checking stats which we should do for running containers as well + queryAll = true + containerFunc = ic.Libpod.GetRunningContainers } go func() { diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 16817ee4c2..cbdade15b9 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -1049,7 +1049,7 @@ func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []stri if options.Latest { return nil, errors.New("latest is not supported for the remote client") } - return containers.Stats(ic.ClientCtx, namesOrIds, new(containers.StatsOptions).WithStream(options.Stream).WithInterval(options.Interval)) + return containers.Stats(ic.ClientCtx, namesOrIds, new(containers.StatsOptions).WithStream(options.Stream).WithInterval(options.Interval).WithAll(options.All)) } // ShouldRestart reports back whether the container will restart. diff --git a/test/e2e/stats_test.go b/test/e2e/stats_test.go index 656ef30c4e..8a34cffb00 100644 --- a/test/e2e/stats_test.go +++ b/test/e2e/stats_test.go @@ -264,4 +264,38 @@ var _ = Describe("Podman stats", func() { Expect(err).ToNot(HaveOccurred()) Expect(limit).To(BeNumerically("==", 100*1024*1024)) }) + + It("podman stats --all", func() { + runningContainersession := podmanTest.RunTopContainer("") + runningContainersession.WaitWithDefaultTimeout() + Expect(runningContainersession).Should(Exit(0)) + runningCtrID := runningContainersession.OutputToString()[0:12] + + createdContainerSession, _, _ := podmanTest.CreatePod(map[string][]string{ + "--infra": {"true"}, + }) + + createdContainerSession.WaitWithDefaultTimeout() + Expect(createdContainerSession).Should(Exit(0)) + + sessionAll := podmanTest.Podman([]string{"stats", "--no-stream", "--format", "{{.ID}}"}) + sessionAll.WaitWithDefaultTimeout() + Expect(sessionAll).Should(Exit(0)) + Expect(sessionAll.OutputToString()).Should(Equal(runningCtrID)) + + sessionAll = podmanTest.Podman([]string{"stats", "--no-stream", "--all=false", "--format", "{{.ID}}"}) + sessionAll.WaitWithDefaultTimeout() + Expect(sessionAll).Should(Exit(0)) + Expect(sessionAll.OutputToString()).Should(Equal(runningCtrID)) + + sessionAll = podmanTest.Podman([]string{"stats", "--all=true", "--no-stream", "--format", "{{.ID}}"}) + sessionAll.WaitWithDefaultTimeout() + Expect(sessionAll).Should(Exit(0)) + Expect(sessionAll.OutputToStringArray()).Should(HaveLen(2)) + + sessionAll = podmanTest.Podman([]string{"stats", "--all", "--no-stream", "--format", "{{.ID}}"}) + sessionAll.WaitWithDefaultTimeout() + Expect(sessionAll).Should(Exit(0)) + Expect(sessionAll.OutputToStringArray()).Should(HaveLen(2)) + }) })