From 6a2a1dd6cf3d15d8fc35eece159ab2a9ffd22058 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Mon, 27 Oct 2025 15:34:14 +0100 Subject: [PATCH] api/types/container: StatsResponse: add OSType field Adds a per-stats OSType field to allow handle the platform-specific fields. Before this change, the client had to get the OSType field from the server's API response header and copy it to each record. Older daemon versions don't have this field, so the client still needs to handle fallbacks. Signed-off-by: Sebastiaan van Stijn --- api/docs/CHANGELOG.md | 2 ++ api/docs/v1.52.yaml | 7 +++++++ api/swagger.yaml | 7 +++++++ api/types/container/stats.go | 4 ++++ daemon/stats.go | 10 ++++++---- daemon/stats/collector.go | 6 ++++-- daemon/stats_unix.go | 8 +++++--- daemon/stats_windows.go | 4 +++- .../github.com/moby/moby/api/types/container/stats.go | 4 ++++ 9 files changed, 42 insertions(+), 10 deletions(-) diff --git a/api/docs/CHANGELOG.md b/api/docs/CHANGELOG.md index 16fc2b68bc..cf5c92c2ab 100644 --- a/api/docs/CHANGELOG.md +++ b/api/docs/CHANGELOG.md @@ -69,6 +69,8 @@ keywords: "API, Docker, rcli, REST, documentation" of the `Resource` requirements. * `GET /tasks/{id}` now returns `SwapBytes` and `MemorySwappiness` fields as part of the `Resource` requirements. +* `GET /containers/{id}/stats` now returns an `os_type` field to allow platform- + specific handling of the stats. ## v1.51 API changes diff --git a/api/docs/v1.52.yaml b/api/docs/v1.52.yaml index 1f5ea81695..ee14466e0d 100644 --- a/api/docs/v1.52.yaml +++ b/api/docs/v1.52.yaml @@ -5639,6 +5639,13 @@ definitions: type: "string" x-nullable: true example: "boring_wozniak" + os_type: + description: | + OSType is the OS of the container ("linux" or "windows") to allow + platform-specific handling of stats. + type: "string" + x-nullable: true + example: "linux" read: description: | Date and time at which this sample was collected. diff --git a/api/swagger.yaml b/api/swagger.yaml index 1f5ea81695..ee14466e0d 100644 --- a/api/swagger.yaml +++ b/api/swagger.yaml @@ -5639,6 +5639,13 @@ definitions: type: "string" x-nullable: true example: "boring_wozniak" + os_type: + description: | + OSType is the OS of the container ("linux" or "windows") to allow + platform-specific handling of stats. + type: "string" + x-nullable: true + example: "linux" read: description: | Date and time at which this sample was collected. diff --git a/api/types/container/stats.go b/api/types/container/stats.go index 02133d44d0..6a34f6ab76 100644 --- a/api/types/container/stats.go +++ b/api/types/container/stats.go @@ -155,6 +155,10 @@ type StatsResponse struct { // Name is the name of the container for which the stats were collected. Name string `json:"name,omitempty"` + // OSType is the OS of the container ("linux" or "windows") to allow + // platform-specific handling of stats. + OSType string `json:"os_type,omitempty"` + // Read is the date and time at which this sample was collected. Read time.Time `json:"read"` diff --git a/daemon/stats.go b/daemon/stats.go index caaa1af966..5d6550e420 100644 --- a/daemon/stats.go +++ b/daemon/stats.go @@ -29,8 +29,9 @@ func (daemon *Daemon) ContainerStats(ctx context.Context, prefixOrName string, c if !ctr.State.IsRunning() || ctr.State.IsRestarting() { // The container is either not running or restarting, return an empty stats. return json.NewEncoder(config.OutStream()).Encode(&containertypes.StatsResponse{ - Name: ctr.Name, - ID: ctr.ID, + ID: ctr.ID, + Name: ctr.Name, + OSType: runtime.GOOS, }) } if config.OneShot { @@ -133,8 +134,9 @@ done: if cerrdefs.IsNotFound(err) || cerrdefs.IsConflict(err) { // return empty stats containing only name and ID if not running or not found return &containertypes.StatsResponse{ - Name: ctr.Name, - ID: ctr.ID, + ID: ctr.ID, + Name: ctr.Name, + OSType: runtime.GOOS, }, nil } log.G(context.TODO()).Errorf("collecting stats for container %s: %v", ctr.Name, err) diff --git a/daemon/stats/collector.go b/daemon/stats/collector.go index 0735d17c03..adae57c5f2 100644 --- a/daemon/stats/collector.go +++ b/daemon/stats/collector.go @@ -1,6 +1,7 @@ package stats import ( + "runtime" "sync" "time" @@ -109,8 +110,9 @@ func (s *Collector) Run() { stats, err := s.supervisor.GetContainerStats(pair.container) if err != nil { stats = &containertypes.StatsResponse{ - Name: pair.container.Name, - ID: pair.container.ID, + ID: pair.container.ID, + Name: pair.container.Name, + OSType: runtime.GOOS, } } pair.publisher.Publish(*stats) diff --git a/daemon/stats_unix.go b/daemon/stats_unix.go index 33c535d06e..bceb6c7b7e 100644 --- a/daemon/stats_unix.go +++ b/daemon/stats_unix.go @@ -8,6 +8,7 @@ import ( "fmt" "io" "os" + "runtime" "strconv" "strings" @@ -47,9 +48,10 @@ func (daemon *Daemon) stats(c *container.Container) (*containertypes.StatsRespon return nil, err } s := &containertypes.StatsResponse{ - Name: c.Name, - ID: c.ID, - Read: cs.Read, + ID: c.ID, + Name: c.Name, + OSType: runtime.GOOS, + Read: cs.Read, } switch t := cs.Metrics.(type) { case *statsV1.Metrics: diff --git a/daemon/stats_windows.go b/daemon/stats_windows.go index ff4eafc65b..e2c6ec0043 100644 --- a/daemon/stats_windows.go +++ b/daemon/stats_windows.go @@ -2,6 +2,7 @@ package daemon import ( "context" + "runtime" cerrdefs "github.com/containerd/errdefs" containertypes "github.com/moby/moby/api/types/container" @@ -28,8 +29,9 @@ func (daemon *Daemon) stats(c *container.Container) (*containertypes.StatsRespon // Start with an empty structure s := &containertypes.StatsResponse{ - Name: c.Name, ID: c.ID, + Name: c.Name, + OSType: runtime.GOOS, Read: stats.Read, NumProcs: platform.NumProcs(), } diff --git a/vendor/github.com/moby/moby/api/types/container/stats.go b/vendor/github.com/moby/moby/api/types/container/stats.go index 02133d44d0..6a34f6ab76 100644 --- a/vendor/github.com/moby/moby/api/types/container/stats.go +++ b/vendor/github.com/moby/moby/api/types/container/stats.go @@ -155,6 +155,10 @@ type StatsResponse struct { // Name is the name of the container for which the stats were collected. Name string `json:"name,omitempty"` + // OSType is the OS of the container ("linux" or "windows") to allow + // platform-specific handling of stats. + OSType string `json:"os_type,omitempty"` + // Read is the date and time at which this sample was collected. Read time.Time `json:"read"`