diff options
author | openshift-ci[bot] <75433959+openshift-ci[bot]@users.noreply.github.com> | 2021-08-04 09:56:57 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-08-04 09:56:57 +0000 |
commit | 3a922cbc99f49b996c0379a9ec90ccc47a325018 (patch) | |
tree | dab0fdf142508614c6b17cc0d277ae91c58d756a | |
parent | 1bc8c94409c1c9dc47b4569833af534beba9333f (diff) | |
parent | 34b28d95986a08bdd74dd89ce6647458cda75731 (diff) | |
download | podman-3a922cbc99f49b996c0379a9ec90ccc47a325018.tar.gz podman-3a922cbc99f49b996c0379a9ec90ccc47a325018.tar.bz2 podman-3a922cbc99f49b996c0379a9ec90ccc47a325018.zip |
Merge pull request #11003 from pascomnet/f_stats
stats: add a interval parameter to cli and api stats streaming
-rw-r--r-- | cmd/podman/containers/stats.go | 13 | ||||
-rw-r--r-- | docs/source/markdown/podman-stats.1.md | 4 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/containers_stats.go | 10 | ||||
-rw-r--r-- | pkg/api/server/register_containers.go | 5 | ||||
-rw-r--r-- | pkg/bindings/containers/containers.go | 3 | ||||
-rw-r--r-- | pkg/bindings/containers/types.go | 3 | ||||
-rw-r--r-- | pkg/bindings/containers/types_stats_options.go | 16 | ||||
-rw-r--r-- | pkg/domain/entities/containers.go | 2 | ||||
-rw-r--r-- | pkg/domain/infra/abi/containers.go | 5 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/containers.go | 2 | ||||
-rw-r--r-- | test/e2e/pod_stats_test.go | 22 | ||||
-rw-r--r-- | test/e2e/stats_test.go | 44 |
12 files changed, 100 insertions, 29 deletions
diff --git a/cmd/podman/containers/stats.go b/cmd/podman/containers/stats.go index 208d5d58f..11e8f6870 100644 --- a/cmd/podman/containers/stats.go +++ b/cmd/podman/containers/stats.go @@ -5,6 +5,7 @@ import ( "os" tm "github.com/buger/goterm" + "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/report" "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" @@ -16,7 +17,6 @@ import ( "github.com/containers/podman/v3/utils" "github.com/docker/go-units" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -55,6 +55,7 @@ type statsOptionsCLI struct { Latest bool NoReset bool NoStream bool + Interval int } var ( @@ -72,6 +73,9 @@ func statFlags(cmd *cobra.Command) { flags.BoolVar(&statsOptions.NoReset, "no-reset", false, "Disable resetting the screen between intervals") flags.BoolVar(&statsOptions.NoStream, "no-stream", false, "Disable streaming stats and only pull the first result, default setting is false") + intervalFlagName := "interval" + flags.IntVarP(&statsOptions.Interval, intervalFlagName, "i", 5, "Time in seconds between stats reports") + _ = cmd.RegisterFlagCompletionFunc(intervalFlagName, completion.AutocompleteNone) } func init() { @@ -122,8 +126,9 @@ func stats(cmd *cobra.Command, args []string) error { // Convert to the entities options. We should not leak CLI-only // options into the backend and separate concerns. opts := entities.ContainerStatsOptions{ - Latest: statsOptions.Latest, - Stream: !statsOptions.NoStream, + Latest: statsOptions.Latest, + Stream: !statsOptions.NoStream, + Interval: statsOptions.Interval, } statsChan, err := registry.ContainerEngine().ContainerStats(registry.Context(), args, opts) if err != nil { @@ -134,7 +139,7 @@ func stats(cmd *cobra.Command, args []string) error { return report.Error } if err := outputStats(report.Stats); err != nil { - logrus.Error(err) + return err } } return nil diff --git a/docs/source/markdown/podman-stats.1.md b/docs/source/markdown/podman-stats.1.md index 300106796..abd8fd530 100644 --- a/docs/source/markdown/podman-stats.1.md +++ b/docs/source/markdown/podman-stats.1.md @@ -37,6 +37,10 @@ Do not clear the terminal/screen in between reporting intervals Disable streaming stats and only pull the first result, default setting is false +#### **--interval**=*seconds*, **-i**=*seconds* + +Time in seconds between stats reports, defaults to 5 seconds. + #### **--format**=*template* Pretty-print container statistics to JSON or using a Go template diff --git a/pkg/api/handlers/libpod/containers_stats.go b/pkg/api/handlers/libpod/containers_stats.go index 22faab71f..8a04884b0 100644 --- a/pkg/api/handlers/libpod/containers_stats.go +++ b/pkg/api/handlers/libpod/containers_stats.go @@ -3,7 +3,6 @@ package libpod import ( "encoding/json" "net/http" - "time" "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers/utils" @@ -16,8 +15,6 @@ import ( "github.com/sirupsen/logrus" ) -const DefaultStatsPeriod = 5 * time.Second - func StatsContainer(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value("runtime").(*libpod.Runtime) decoder := r.Context().Value("decoder").(*schema.Decoder) @@ -35,8 +32,10 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { query := struct { Containers []string `schema:"containers"` Stream bool `schema:"stream"` + Interval int `schema:"interval"` }{ - Stream: true, + Stream: true, + Interval: 5, } if err := decoder.Decode(&query, r.URL.Query()); err != nil { utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) @@ -48,7 +47,8 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { containerEngine := abi.ContainerEngine{Libpod: runtime} statsOptions := entities.ContainerStatsOptions{ - Stream: query.Stream, + Stream: query.Stream, + Interval: query.Interval, } // Stats will stop if the connection is closed. diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go index 89de84cce..0ec4f95d9 100644 --- a/pkg/api/server/register_containers.go +++ b/pkg/api/server/register_containers.go @@ -1108,6 +1108,11 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // type: boolean // default: true // description: Stream the output + // - in: query + // name: interval + // type: integer + // default: 5 + // description: Time in seconds between stats reports // produces: // - application/json // responses: diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go index 86304f392..bc7b0c8c9 100644 --- a/pkg/bindings/containers/containers.go +++ b/pkg/bindings/containers/containers.go @@ -223,6 +223,9 @@ func Stats(ctx context.Context, containers []string, options *StatsOptions) (cha if err != nil { return nil, err } + if !response.IsSuccess() { + return nil, response.Process(nil) + } statsChan := make(chan entities.ContainerStatsReport) diff --git a/pkg/bindings/containers/types.go b/pkg/bindings/containers/types.go index cf088441f..3e9a384de 100644 --- a/pkg/bindings/containers/types.go +++ b/pkg/bindings/containers/types.go @@ -166,7 +166,8 @@ type StartOptions struct { //go:generate go run ../generator/generator.go StatsOptions // StatsOptions are optional options for getting stats on containers type StatsOptions struct { - Stream *bool + Stream *bool + Interval *int } //go:generate go run ../generator/generator.go TopOptions diff --git a/pkg/bindings/containers/types_stats_options.go b/pkg/bindings/containers/types_stats_options.go index 8f6a03301..604004eb6 100644 --- a/pkg/bindings/containers/types_stats_options.go +++ b/pkg/bindings/containers/types_stats_options.go @@ -35,3 +35,19 @@ func (o *StatsOptions) GetStream() bool { } return *o.Stream } + +// WithInterval +func (o *StatsOptions) WithInterval(value int) *StatsOptions { + v := &value + o.Interval = v + return o +} + +// GetInterval +func (o *StatsOptions) GetInterval() int { + var interval int + if o.Interval == nil { + return interval + } + return *o.Interval +} diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index 564921c52..d2a7505a8 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -440,6 +440,8 @@ type ContainerStatsOptions struct { Latest bool // Stream stats. Stream bool + // Interval in seconds + Interval int } // ContainerStatsReport is used for streaming container stats. diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 2003879b8..ddd768328 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -1283,6 +1283,9 @@ func (ic *ContainerEngine) Shutdown(_ context.Context) { } func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []string, options entities.ContainerStatsOptions) (statsChan chan entities.ContainerStatsReport, err error) { + if options.Interval < 1 { + return nil, errors.New("Invalid interval, must be a positive number greater zero") + } statsChan = make(chan entities.ContainerStatsReport, 1) containerFunc := ic.Libpod.GetRunningContainers @@ -1363,7 +1366,7 @@ func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []stri return } - time.Sleep(time.Second) + time.Sleep(time.Second * time.Duration(options.Interval)) goto stream }() diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 58f9c5fb0..3c2802165 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -873,7 +873,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)) + return containers.Stats(ic.ClientCtx, namesOrIds, new(containers.StatsOptions).WithStream(options.Stream).WithInterval(options.Interval)) } // ShouldRestart reports back whether the container will restart diff --git a/test/e2e/pod_stats_test.go b/test/e2e/pod_stats_test.go index 46043b16d..5ec209034 100644 --- a/test/e2e/pod_stats_test.go +++ b/test/e2e/pod_stats_test.go @@ -37,19 +37,19 @@ var _ = Describe("Podman pod stats", func() { processTestResult(f) }) - It("podman stats should run with no pods", func() { + It("podman pod stats should run with no pods", func() { session := podmanTest.Podman([]string{"pod", "stats", "--no-stream"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) }) - It("podman stats with a bogus pod", func() { + It("podman pod stats with a bogus pod", func() { session := podmanTest.Podman([]string{"pod", "stats", "foobar"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(125)) }) - It("podman stats on a specific running pod", func() { + It("podman pod stats on a specific running pod", func() { _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) @@ -66,7 +66,7 @@ var _ = Describe("Podman pod stats", func() { Expect(stats).Should(Exit(0)) }) - It("podman stats on a specific running pod with shortID", func() { + It("podman pod stats on a specific running pod with shortID", func() { _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) @@ -83,7 +83,7 @@ var _ = Describe("Podman pod stats", func() { Expect(stats).Should(Exit(0)) }) - It("podman stats on a specific running pod with name", func() { + It("podman pod stats on a specific running pod with name", func() { _, ec, podid := podmanTest.CreatePod(map[string][]string{"--name": {"test"}}) Expect(ec).To(Equal(0)) @@ -100,7 +100,7 @@ var _ = Describe("Podman pod stats", func() { Expect(stats).Should(Exit(0)) }) - It("podman stats on running pods", func() { + It("podman pod stats on running pods", func() { _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) @@ -117,7 +117,7 @@ var _ = Describe("Podman pod stats", func() { Expect(stats).Should(Exit(0)) }) - It("podman stats on all pods", func() { + It("podman pod stats on all pods", func() { _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) @@ -134,7 +134,7 @@ var _ = Describe("Podman pod stats", func() { Expect(stats).Should(Exit(0)) }) - It("podman stats with json output", func() { + It("podman pod stats with json output", func() { _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) @@ -151,7 +151,7 @@ var _ = Describe("Podman pod stats", func() { Expect(stats).Should(Exit(0)) Expect(stats.IsJSONOutputValid()).To(BeTrue()) }) - It("podman stats with GO template", func() { + It("podman pod stats with GO template", func() { _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) @@ -163,7 +163,7 @@ var _ = Describe("Podman pod stats", func() { Expect(stats).To(Exit(0)) }) - It("podman stats with invalid GO template", func() { + It("podman pod stats with invalid GO template", func() { _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) @@ -175,7 +175,7 @@ var _ = Describe("Podman pod stats", func() { Expect(stats).To(ExitWithError()) }) - It("podman stats on net=host post", func() { + It("podman pod stats on net=host post", func() { SkipIfRootless("--net=host not supported for rootless pods at present") podName := "testPod" podCreate := podmanTest.Podman([]string{"pod", "create", "--net=host", "--name", podName}) diff --git a/test/e2e/stats_test.go b/test/e2e/stats_test.go index e32d515a0..a0be5d462 100644 --- a/test/e2e/stats_test.go +++ b/test/e2e/stats_test.go @@ -1,5 +1,3 @@ -// +build - package integration import ( @@ -84,15 +82,49 @@ var _ = Describe("Podman stats", func() { Expect(session).Should(Exit(0)) }) - It("podman stats only output CPU data", func() { + It("podman stats with GO template", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - session = podmanTest.Podman([]string{"stats", "--all", "--no-stream", "--format", "\"{{.ID}} {{.UpTime}} {{.AVGCPU}}\""}) + stats := podmanTest.Podman([]string{"stats", "-a", "--no-reset", "--no-stream", "--format", "table {{.ID}} {{.AVGCPU}} {{.MemUsage}} {{.CPU}} {{.NetIO}} {{.BlockIO}} {{.PIDS}}"}) + stats.WaitWithDefaultTimeout() + Expect(stats).To(Exit(0)) + }) + + It("podman stats with invalid GO template", func() { + session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.LineInOutputContains("UpTime")).To(BeTrue()) - Expect(session.LineInOutputContains("AVGCPU")).To(BeTrue()) Expect(session).Should(Exit(0)) + stats := podmanTest.Podman([]string{"stats", "-a", "--no-reset", "--no-stream", "--format", "\"table {{.ID}} {{.NoSuchField}} \""}) + stats.WaitWithDefaultTimeout() + Expect(stats).To(ExitWithError()) + }) + + It("podman stats with negative interval", func() { + session := podmanTest.RunTopContainer("") + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + stats := podmanTest.Podman([]string{"stats", "-a", "--no-reset", "--no-stream", "--interval=-1"}) + stats.WaitWithDefaultTimeout() + Expect(stats).To(ExitWithError()) + }) + + It("podman stats with zero interval", func() { + session := podmanTest.RunTopContainer("") + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + stats := podmanTest.Podman([]string{"stats", "-a", "--no-reset", "--no-stream", "--interval=0"}) + stats.WaitWithDefaultTimeout() + Expect(stats).To(ExitWithError()) + }) + + It("podman stats with interval", func() { + session := podmanTest.RunTopContainer("") + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + stats := podmanTest.Podman([]string{"stats", "-a", "--no-reset", "--no-stream", "--interval=5"}) + stats.WaitWithDefaultTimeout() + Expect(stats).Should(Exit(0)) }) It("podman stats with json output", func() { |