summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoropenshift-ci[bot] <75433959+openshift-ci[bot]@users.noreply.github.com>2021-08-04 09:56:57 +0000
committerGitHub <noreply@github.com>2021-08-04 09:56:57 +0000
commit3a922cbc99f49b996c0379a9ec90ccc47a325018 (patch)
treedab0fdf142508614c6b17cc0d277ae91c58d756a
parent1bc8c94409c1c9dc47b4569833af534beba9333f (diff)
parent34b28d95986a08bdd74dd89ce6647458cda75731 (diff)
downloadpodman-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.go13
-rw-r--r--docs/source/markdown/podman-stats.1.md4
-rw-r--r--pkg/api/handlers/libpod/containers_stats.go10
-rw-r--r--pkg/api/server/register_containers.go5
-rw-r--r--pkg/bindings/containers/containers.go3
-rw-r--r--pkg/bindings/containers/types.go3
-rw-r--r--pkg/bindings/containers/types_stats_options.go16
-rw-r--r--pkg/domain/entities/containers.go2
-rw-r--r--pkg/domain/infra/abi/containers.go5
-rw-r--r--pkg/domain/infra/tunnel/containers.go2
-rw-r--r--test/e2e/pod_stats_test.go22
-rw-r--r--test/e2e/stats_test.go44
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() {