diff --git a/pkg/api/handlers/libpod/containers_stats.go b/pkg/api/handlers/libpod/containers_stats.go index 41b0bfbaf7..2ed2006c91 100644 --- a/pkg/api/handlers/libpod/containers_stats.go +++ b/pkg/api/handlers/libpod/containers_stats.go @@ -62,18 +62,23 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { return } - // Write header and content type. - w.WriteHeader(http.StatusOK) - w.Header().Set("Content-Type", "application/json") - if flusher, ok := w.(http.Flusher); ok { - flusher.Flush() - } - + wroteContent := false // Set up JSON encoder for streaming. coder := json.NewEncoder(w) coder.SetEscapeHTML(true) for stats := range statsChan { + if !wroteContent { + if stats.Error != nil { + utils.ContainerNotFound(w, "", stats.Error) + return + } + // Write header and content type. + w.WriteHeader(http.StatusOK) + w.Header().Set("Content-Type", "application/json") + wroteContent = true + } + if err := coder.Encode(stats); err != nil { // Note: even when streaming, the stats goroutine will // be notified (and stop) as the connection will be diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index fb417acfa2..4f0fd63094 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -1571,12 +1571,7 @@ func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []stri go func() { defer close(statsChan) - var ( - err error - containers []*libpod.Container - containerStats map[string]*define.ContainerStats - ) - containerStats = make(map[string]*define.ContainerStats) + containerStats := make(map[string]*define.ContainerStats) stream: // label to flatten the scope select { @@ -1590,7 +1585,7 @@ func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []stri // Anonymous func to easily use the return values for streaming. computeStats := func() ([]define.ContainerStats, error) { - containers, err = containerFunc() + containers, err := containerFunc() if err != nil { return nil, fmt.Errorf("unable to get list of containers: %w", err) } diff --git a/test/apiv2/20-containers.at b/test/apiv2/20-containers.at index e7ffefa1da..d73187650b 100644 --- a/test/apiv2/20-containers.at +++ b/test/apiv2/20-containers.at @@ -112,21 +112,8 @@ t GET libpod/containers/json?last=1 200 \ cid=$(jq -r '.[0].Id' <<<"$output") -if root; then - t GET libpod/containers/stats?containers='[$cid]' 200 -else - if have_cgroupsv2; then - t GET libpod/containers/stats?containers='[$cid]' 200 - else - t GET libpod/containers/stats?containers='[$cid]' 409 - fi -fi - -# max_usage is not set for cgroupv2 -if have_cgroupsv2; then - t GET libpod/containers/stats?containers='[$cid]' 200 \ - .memory_stats.max_usage=null -fi +t GET "libpod/containers/stats?containers=$cid&stream=false" 200 \ + .memory_stats.max_usage=null t DELETE libpod/containers/$cid 200 .[0].Id=$cid @@ -241,20 +228,13 @@ t GET libpod/images/newrepo:v1/json 200 \ cparam="repo=newrepo&tag=v2&comment=bar&author=eric" cparam="$cparam&format=docker&changes=CMD=/bin/foo" -if root || have_cgroupsv2; then - t POST "libpod/commit?container=${cid:0:12}&$cparam&pause=true" 200 - t GET libpod/images/newrepo:v2/json 200 \ - .RepoTags[0]=localhost/newrepo:v2 \ - .Author=eric \ - .Comment=bar \ - .Config.Cmd[-1]="/bin/foo" - t DELETE images/localhost/newrepo:v2?force=true 200 -else - # cgroupsv1 rootless : pause is not supported in cgroups v1 rootless - t POST "libpod/commit?container=${cid:0:12}&$cparam&pause=true" 500 \ - .cause="this container does not have a cgroup" \ - .message~".*pause containers on rootless containers with cgroup V1" -fi +t POST "libpod/commit?container=${cid:0:12}&$cparam&pause=true" 200 +t GET libpod/images/newrepo:v2/json 200 \ + .RepoTags[0]=localhost/newrepo:v2 \ + .Author=eric \ + .Comment=bar \ + .Config.Cmd[-1]="/bin/foo" +t DELETE images/localhost/newrepo:v2?force=true 200 # Create a container for testing the container initializing later podman create -t -i --name myctr $IMAGE ls @@ -484,24 +464,8 @@ t POST containers/prune?filters='{"network":["anynetwork"]}' 500 \ # Test CPU limit (NanoCPUs) nanoCpu=500000 -if have_cgroupsv2; then - t POST containers/create Image=$IMAGE HostConfig='{"NanoCpus":500000}' 201 \ - .Id~[0-9a-f]\\{64\\} -else - if root; then - # cgroupsv1 rootful : NanoCpus needs to set more than 10000000 - t POST containers/create Image=$IMAGE HostConfig='{"NanoCpus":500000}' 500 \ - .cause="CPU cfs quota cannot be less than 1ms (i.e. 1000)" - t POST containers/create Image=$IMAGE HostConfig='{"NanoCpus":10000000}' 201 \ - .Id~[0-9a-f]\\{64\\} - nanoCpu=10000000 - else - # cgroupsv1 rootless : Resource limits that include NanoCPUs are not supported and ignored - t POST containers/create Image=$IMAGE HostConfig='{"NanoCpus":500000}' 201 \ - .Id~[0-9a-f]\\{64\\} - nanoCpu=0 - fi -fi +t POST containers/create Image=$IMAGE HostConfig='{"NanoCpus":500000}' 201 \ + .Id~[0-9a-f]\\{64\\} cid=$(jq -r '.Id' <<<"$output") t GET containers/$cid/json 200 \ @@ -677,14 +641,11 @@ t GET containers/status-test/json 200 .State.Status="created" podman start status-test t GET containers/status-test/json 200 .State.Status="running" -# cgroupsv1 rootless : pause and unpause are not supported in cgroups v1 rootless -if root || have_cgroupsv2; then - podman pause status-test - t GET containers/status-test/json 200 .State.Status="paused" +podman pause status-test +t GET containers/status-test/json 200 .State.Status="paused" - podman unpause status-test - t GET containers/status-test/json 200 .State.Status="running" -fi +podman unpause status-test +t GET containers/status-test/json 200 .State.Status="running" podman stop status-test & sleep 1 @@ -718,11 +679,6 @@ if root; then cgroupPath=/sys/fs/cgroup/cpu.weight # 002 is the byte length cpu_weight_expect=$'\001\0025' - if ! have_cgroupsv2; then - cgroupPath=/sys/fs/cgroup/cpu/cpu.shares - # 004 is the byte length - cpu_weight_expect=$'\001\004123' - fi # Verify echo '{ "AttachStdout":true,"Cmd":["cat", "'$cgroupPath'"]}' >${TMPD}/exec.json diff --git a/test/e2e/stats_test.go b/test/e2e/stats_test.go index 3ab2ea3ed7..3f0d4a8f16 100644 --- a/test/e2e/stats_test.go +++ b/test/e2e/stats_test.go @@ -24,12 +24,7 @@ var _ = Describe("Podman stats", func() { It("podman stats with bogus container", func() { session := podmanTest.Podman([]string{"stats", "--no-stream", "123"}) session.WaitWithDefaultTimeout() - expect := `unable to get list of containers: unable to look up container 123: no container with name or ID "123" found: no such container` - // FIXME: #22612 - if IsRemote() { - expect = "types.ContainerStatsReport.Error: decode non empty interface: can not unmarshal into nil, error found in #9 byte" - } - Expect(session).Should(ExitWithError(125, expect)) + Expect(session).Should(ExitWithError(125, `unable to get list of containers: unable to look up container 123: no container with name or ID "123" found: no such container`)) }) It("podman stats on a running container", func() {