//go:build !remote package compat import ( "encoding/json" "fmt" "net/http" "time" "github.com/containers/podman/v5/libpod" "github.com/containers/podman/v5/libpod/define" "github.com/containers/podman/v5/pkg/api/handlers/utils" api "github.com/containers/podman/v5/pkg/api/types" "github.com/sirupsen/logrus" ) const defaultStatsPeriod = 5 * time.Second func StatsContainer(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) decoder := utils.GetDecoder(r) query := struct { Stream bool `schema:"stream"` OneShot bool `schema:"one-shot"` // added schema for one shot }{ Stream: true, } 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)) return } if query.Stream && query.OneShot { // mismatch. one-shot can only be passed with stream=false utils.Error(w, http.StatusBadRequest, define.ErrInvalidArg) return } name := utils.GetName(r) ctnr, err := runtime.LookupContainer(name) if err != nil { utils.ContainerNotFound(w, name, err) return } stats, err := ctnr.GetContainerStats(nil) if err != nil { utils.InternalServerError(w, fmt.Errorf("failed to obtain Container %s stats: %w", name, err)) return } coder := json.NewEncoder(w) // 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() } // Set up JSON encoder for streaming. coder.SetEscapeHTML(true) var preRead time.Time var preCPUStats CPUStats if query.Stream { preRead = time.Now() preCPUStats = getPreCPUStats(stats) } onlineCPUs, err := libpod.GetOnlineCPUs(ctnr) if err != nil { utils.InternalServerError(w, err) return } streamLabel: // A label to flatten the scope select { case <-r.Context().Done(): logrus.Debugf("Client connection (container stats) cancelled") default: stats, err = ctnr.GetContainerStats(stats) if err != nil { logrus.Errorf("Unable to get container stats: %v", err) return } s, err := statsContainerJSON(ctnr, stats, preCPUStats, onlineCPUs) if err != nil { return } s.Stats.PreRead = preRead var jsonOut interface{} if utils.IsLibpodRequest(r) { jsonOut = s } else { jsonOut = DockerStatsJSON(s) } if err := coder.Encode(jsonOut); err != nil { logrus.Errorf("Unable to encode stats: %v", err) return } if flusher, ok := w.(http.Flusher); ok { flusher.Flush() } if !query.Stream || query.OneShot { return } preRead = s.Read bits, err := json.Marshal(s.CPUStats) if err != nil { logrus.Errorf("Unable to marshal cpu stats: %q", err) } if err := json.Unmarshal(bits, &preCPUStats); err != nil { logrus.Errorf("Unable to unmarshal previous stats: %q", err) } time.Sleep(defaultStatsPeriod) goto streamLabel } }