From a1c9a686b08b4682cf5d927ace61da9ebb4028b7 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Thu, 29 Jun 2023 17:49:09 +0200 Subject: [PATCH] api: use singleflight for /info endpoint Prevent potential suggestion when many concurrent requests happen on the /info endpoint. It's worth noting that with this change, requests to the endpoint while another request is still in flight will share the results, hence might be slightly incorrect (for example, the output includes SystemTime, which may now be incorrect). Assuming that under normal circumstances, requests will still happen fast enough to not be shared, this may not be a problem, but we could decide to update specific fields to not be shared. Signed-off-by: Sebastiaan van Stijn --- api/server/router/system/system.go | 7 +++ api/server/router/system/system_routes.go | 57 ++++++++++++----------- 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/api/server/router/system/system.go b/api/server/router/system/system.go index 8235abf686..0bc85c602b 100644 --- a/api/server/router/system/system.go +++ b/api/server/router/system/system.go @@ -2,7 +2,9 @@ package system // import "github.com/docker/docker/api/server/router/system" import ( "github.com/docker/docker/api/server/router" + "github.com/docker/docker/api/types" buildkit "github.com/docker/docker/builder/builder-next" + "resenje.org/singleflight" ) // systemRouter provides information about the Docker system overall. @@ -13,6 +15,11 @@ type systemRouter struct { routes []router.Route builder *buildkit.Builder features func() map[string]bool + + // collectSystemInfo is a single-flight for the /info endpoint, + // unique per API version (as different API versions may return + // a different API response). + collectSystemInfo singleflight.Group[string, *types.Info] } // NewRouter initializes a new system router diff --git a/api/server/router/system/system_routes.go b/api/server/router/system/system_routes.go index c7902e1f9a..560b972bf9 100644 --- a/api/server/router/system/system_routes.go +++ b/api/server/router/system/system_routes.go @@ -57,38 +57,41 @@ func (s *systemRouter) swarmStatus() string { } func (s *systemRouter) getInfo(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - info := s.backend.SystemInfo() - - if s.cluster != nil { - info.Swarm = s.cluster.Info() - info.Warnings = append(info.Warnings, info.Swarm.Warnings...) - } - version := httputils.VersionFromContext(ctx) - if versions.LessThan(version, "1.25") { - // TODO: handle this conversion in engine-api - kvSecOpts, err := types.DecodeSecurityOptions(info.SecurityOptions) - if err != nil { - info.Warnings = append(info.Warnings, err.Error()) + info, _, _ := s.collectSystemInfo.Do(ctx, version, func(ctx context.Context) (*types.Info, error) { + info := s.backend.SystemInfo() + + if s.cluster != nil { + info.Swarm = s.cluster.Info() + info.Warnings = append(info.Warnings, info.Swarm.Warnings...) } - var nameOnly []string - for _, so := range kvSecOpts { - nameOnly = append(nameOnly, so.Name) + + if versions.LessThan(version, "1.25") { + // TODO: handle this conversion in engine-api + kvSecOpts, err := types.DecodeSecurityOptions(info.SecurityOptions) + if err != nil { + info.Warnings = append(info.Warnings, err.Error()) + } + var nameOnly []string + for _, so := range kvSecOpts { + nameOnly = append(nameOnly, so.Name) + } + info.SecurityOptions = nameOnly + info.ExecutionDriver = "" //nolint:staticcheck // ignore SA1019 (ExecutionDriver is deprecated) } - info.SecurityOptions = nameOnly - info.ExecutionDriver = "" //nolint:staticcheck // ignore SA1019 (ExecutionDriver is deprecated) - } - if versions.LessThan(version, "1.39") { - if info.KernelVersion == "" { - info.KernelVersion = "" + if versions.LessThan(version, "1.39") { + if info.KernelVersion == "" { + info.KernelVersion = "" + } + if info.OperatingSystem == "" { + info.OperatingSystem = "" + } } - if info.OperatingSystem == "" { - info.OperatingSystem = "" + if versions.GreaterThanOrEqualTo(version, "1.42") { + info.KernelMemory = false } - } - if versions.GreaterThanOrEqualTo(version, "1.42") { - info.KernelMemory = false - } + return info, nil + }) return httputils.WriteJSON(w, http.StatusOK, info) }