mirror of
https://github.com/moby/moby.git
synced 2026-01-11 18:51:37 +00:00
api/types/system: remove deprecated Commit.Expected field
This field was deprecated API v1.48 in [moby@ff191c5], and removed in API v1.49 in [moby@564abf9]. This patch: - Removes the field from the API Go types. - Reimplements the `/info` endpoint with the `compat` package to replace the local `infoResponse` implementation. - Removes the `ServiceConfig.ExtraFields` field in api/types/registry introduced in [moby@7d9c50d] to backfill the `AllowNondistributableArtifactsCIDRs` and `AllowNondistributableArtifactsHostnames` fields for API < v1.47. We should also consider deprecating the `ContainerdCommit`, `RuncCommit` and `InitCommit` fields on the `/info` response (as we also include this information as part of the components returned in `/version`), but those can still be useful currently for situations where a user only provides `docker info` output. [moby@ff191c5]:ff191c58f7[moby@564abf9]:564abf9157[moby@7d9c50d]:7d9c50db2bSigned-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
@@ -11,9 +11,6 @@ type ServiceConfig struct {
|
||||
InsecureRegistryCIDRs []netip.Prefix `json:"InsecureRegistryCIDRs"`
|
||||
IndexConfigs map[string]*IndexInfo `json:"IndexConfigs"`
|
||||
Mirrors []string
|
||||
|
||||
// ExtraFields is for internal use to include deprecated fields on older API versions.
|
||||
ExtraFields map[string]any `json:"-"`
|
||||
}
|
||||
|
||||
// IndexInfo contains information about a registry
|
||||
|
||||
@@ -139,11 +139,6 @@ type PluginsInfo struct {
|
||||
type Commit struct {
|
||||
// ID is the actual commit ID or version of external tool.
|
||||
ID string
|
||||
|
||||
// Expected is the commit ID of external tool expected by dockerd as set at build time.
|
||||
//
|
||||
// Deprecated: this field is no longer used in API v1.49, but kept for backward-compatibility with older API versions.
|
||||
Expected string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// NetworkAddressPool is a temp struct used by [Info] struct.
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"maps"
|
||||
|
||||
"github.com/moby/moby/api/types/system"
|
||||
)
|
||||
|
||||
// infoResponse is a wrapper around [system.Info] with a custom
|
||||
// marshal function for legacy fields.
|
||||
type infoResponse struct {
|
||||
*system.Info
|
||||
|
||||
// extraFields is for internal use to include deprecated fields on older API versions.
|
||||
extraFields map[string]any
|
||||
}
|
||||
|
||||
// MarshalJSON implements a custom marshaler to include legacy fields
|
||||
// in API responses.
|
||||
func (ir *infoResponse) MarshalJSON() ([]byte, error) {
|
||||
type tmp *system.Info
|
||||
base, err := json.Marshal((tmp)(ir.Info))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(ir.extraFields) == 0 && (ir.Info == nil || ir.Info.RegistryConfig == nil || len(ir.Info.RegistryConfig.ExtraFields) == 0) {
|
||||
return base, nil
|
||||
}
|
||||
var merged map[string]any
|
||||
_ = json.Unmarshal(base, &merged)
|
||||
|
||||
// Merge top-level extraFields
|
||||
maps.Copy(merged, ir.extraFields)
|
||||
|
||||
// Merge RegistryConfig.ExtraFields if present
|
||||
if ir.Info != nil && ir.Info.RegistryConfig != nil && len(ir.Info.RegistryConfig.ExtraFields) > 0 {
|
||||
if rc, ok := merged["RegistryConfig"].(map[string]any); ok {
|
||||
maps.Copy(rc, ir.Info.RegistryConfig.ExtraFields)
|
||||
}
|
||||
}
|
||||
|
||||
return json.Marshal(merged)
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/moby/moby/api/types/registry"
|
||||
"github.com/moby/moby/api/types/system"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
func TestLegacyFields(t *testing.T) {
|
||||
infoResp := &infoResponse{
|
||||
Info: &system.Info{
|
||||
Containers: 10,
|
||||
},
|
||||
extraFields: map[string]any{
|
||||
"LegacyFoo": false,
|
||||
"LegacyBar": true,
|
||||
},
|
||||
}
|
||||
|
||||
data, err := json.MarshalIndent(infoResp, "", " ")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if expected := `"LegacyFoo": false`; !strings.Contains(string(data), expected) {
|
||||
t.Errorf("legacy fields should contain %s: %s", expected, string(data))
|
||||
}
|
||||
if expected := `"LegacyBar": true`; !strings.Contains(string(data), expected) {
|
||||
t.Errorf("legacy fields should contain %s: %s", expected, string(data))
|
||||
}
|
||||
}
|
||||
|
||||
// TestMarshalRegistryConfigLegacyFields verifies extra fields in the registry config
|
||||
// field in the info response are serialized if they are not empty.
|
||||
// This is used for backwards compatibility for API versions < 1.47.
|
||||
func TestMarshalRegistryConfigLegacyFields(t *testing.T) {
|
||||
expected := []string{"AllowNondistributableArtifactsCIDRs", "AllowNondistributableArtifactsHostnames"}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
info *infoResponse
|
||||
assert func(t *testing.T, data []byte, err error)
|
||||
}{
|
||||
{
|
||||
name: "without legacy fields",
|
||||
info: &infoResponse{
|
||||
Info: &system.Info{},
|
||||
},
|
||||
assert: func(t *testing.T, data []byte, err error) {
|
||||
assert.NilError(t, err)
|
||||
|
||||
var resp map[string]any
|
||||
err = json.Unmarshal(data, &resp)
|
||||
assert.NilError(t, err)
|
||||
|
||||
rc, ok := resp["RegistryConfig"]
|
||||
assert.Check(t, ok)
|
||||
|
||||
for _, v := range expected {
|
||||
assert.Check(t, !is.Contains(rc, v)().Success())
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with legacy fields",
|
||||
info: &infoResponse{
|
||||
Info: &system.Info{
|
||||
RegistryConfig: ®istry.ServiceConfig{
|
||||
ExtraFields: map[string]any{
|
||||
"AllowNondistributableArtifactsCIDRs": json.RawMessage(nil),
|
||||
"AllowNondistributableArtifactsHostnames": json.RawMessage(nil),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
assert: func(t *testing.T, data []byte, err error) {
|
||||
assert.NilError(t, err)
|
||||
|
||||
var resp map[string]any
|
||||
err = json.Unmarshal(data, &resp)
|
||||
assert.NilError(t, err)
|
||||
|
||||
rc, ok := resp["RegistryConfig"]
|
||||
assert.Check(t, ok)
|
||||
|
||||
for _, v := range expected {
|
||||
assert.Check(t, is.Contains(rc, v))
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
data, err := json.MarshalIndent(tc.info, "", " ")
|
||||
tc.assert(t, data, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"github.com/moby/moby/v2/daemon/internal/compat"
|
||||
"github.com/moby/moby/v2/daemon/server/router"
|
||||
"resenje.org/singleflight"
|
||||
)
|
||||
@@ -17,7 +18,7 @@ type systemRouter struct {
|
||||
// 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, *infoResponse]
|
||||
collectSystemInfo singleflight.Group[string, *compat.Wrapper]
|
||||
}
|
||||
|
||||
// NewRouter initializes a new system router
|
||||
|
||||
@@ -64,7 +64,7 @@ func (s *systemRouter) swarmStatus() string {
|
||||
|
||||
func (s *systemRouter) getInfo(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
version := httputils.VersionFromContext(ctx)
|
||||
info, _, _ := s.collectSystemInfo.Do(ctx, version, func(ctx context.Context) (*infoResponse, error) {
|
||||
info, _, _ := s.collectSystemInfo.Do(ctx, version, func(ctx context.Context) (*compat.Wrapper, error) {
|
||||
info, err := s.backend.SystemInfo(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -75,6 +75,7 @@ func (s *systemRouter) getInfo(ctx context.Context, w http.ResponseWriter, r *ht
|
||||
info.Warnings = append(info.Warnings, info.Swarm.Warnings...)
|
||||
}
|
||||
|
||||
var legacyOptions []compat.Option
|
||||
if versions.LessThan(version, "1.44") {
|
||||
for k, rt := range info.Runtimes {
|
||||
// Status field introduced in API v1.44.
|
||||
@@ -88,35 +89,36 @@ func (s *systemRouter) getInfo(ctx context.Context, w http.ResponseWriter, r *ht
|
||||
if versions.LessThan(version, "1.47") {
|
||||
// Field is omitted in API 1.48 and up, but should still be included
|
||||
// in older versions, even if no values are set.
|
||||
info.RegistryConfig.ExtraFields = map[string]any{
|
||||
"AllowNondistributableArtifactsCIDRs": json.RawMessage(nil),
|
||||
"AllowNondistributableArtifactsHostnames": json.RawMessage(nil),
|
||||
}
|
||||
legacyOptions = append(legacyOptions, compat.WithExtraFields(map[string]any{
|
||||
"RegistryConfig": map[string]any{
|
||||
"AllowNondistributableArtifactsCIDRs": json.RawMessage(nil),
|
||||
"AllowNondistributableArtifactsHostnames": json.RawMessage(nil),
|
||||
},
|
||||
}))
|
||||
}
|
||||
if versions.LessThan(version, "1.49") {
|
||||
// FirewallBackend field introduced in API v1.49.
|
||||
info.FirewallBackend = nil
|
||||
}
|
||||
|
||||
extraFields := map[string]any{}
|
||||
if versions.LessThan(version, "1.49") {
|
||||
// Expected commits are omitted in API 1.49, but should still be
|
||||
// included in older versions.
|
||||
info.ContainerdCommit.Expected = info.ContainerdCommit.ID //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.49.
|
||||
info.RuncCommit.Expected = info.RuncCommit.ID //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.49.
|
||||
info.InitCommit.Expected = info.InitCommit.ID //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.49.
|
||||
legacyOptions = append(legacyOptions, compat.WithExtraFields(map[string]any{
|
||||
"ContainerdCommit": map[string]any{"Expected": info.ContainerdCommit.ID},
|
||||
"RuncCommit": map[string]any{"Expected": info.RuncCommit.ID},
|
||||
"InitCommit": map[string]any{"Expected": info.InitCommit.ID},
|
||||
}))
|
||||
}
|
||||
if versions.LessThan(version, "1.50") {
|
||||
info.DiscoveredDevices = nil
|
||||
|
||||
// These fields are omitted in > API 1.49, and always false
|
||||
// older API versions.
|
||||
extraFields = map[string]any{
|
||||
legacyOptions = append(legacyOptions, compat.WithExtraFields(map[string]any{
|
||||
"BridgeNfIptables": json.RawMessage("false"),
|
||||
"BridgeNfIp6tables": json.RawMessage("false"),
|
||||
}
|
||||
}))
|
||||
}
|
||||
return &infoResponse{Info: info, extraFields: extraFields}, nil
|
||||
return compat.Wrap(info, legacyOptions...), nil
|
||||
})
|
||||
|
||||
return httputils.WriteJSON(w, http.StatusOK, info)
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/moby/moby/v2/internal/testutil/request"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
@@ -17,38 +16,63 @@ import (
|
||||
func TestInfoBinaryCommits(t *testing.T) {
|
||||
ctx := setupTest(t)
|
||||
|
||||
t.Run("current", func(t *testing.T) {
|
||||
apiClient := testEnv.APIClient()
|
||||
// API v1.48 and lower returned both the "current" commit (ID) and "expected" commit.
|
||||
// The "Expected" field has been removed in the API types, so define
|
||||
// an ad-hoc type for this test.
|
||||
type legacyCommit struct {
|
||||
ID string
|
||||
Expected string
|
||||
}
|
||||
|
||||
info, err := apiClient.Info(ctx)
|
||||
type legacyInfo struct {
|
||||
ContainerdCommit legacyCommit
|
||||
RuncCommit legacyCommit
|
||||
InitCommit legacyCommit
|
||||
}
|
||||
|
||||
t.Run("current", func(t *testing.T) {
|
||||
res, body, err := request.Get(ctx, "/info", request.JSON)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, res.StatusCode, http.StatusOK)
|
||||
|
||||
buf, err := request.ReadBody(body)
|
||||
assert.NilError(t, err)
|
||||
|
||||
var info legacyInfo
|
||||
err = json.Unmarshal(buf, &info)
|
||||
assert.NilError(t, err)
|
||||
|
||||
assert.Check(t, info.ContainerdCommit.ID != "N/A")
|
||||
assert.Check(t, is.Equal(info.ContainerdCommit.Expected, "")) //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.49.
|
||||
assert.Check(t, is.Equal(info.ContainerdCommit.Expected, ""))
|
||||
|
||||
assert.Check(t, info.InitCommit.ID != "N/A")
|
||||
assert.Check(t, is.Equal(info.InitCommit.Expected, "")) //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.49.
|
||||
assert.Check(t, is.Equal(info.InitCommit.Expected, ""))
|
||||
|
||||
assert.Check(t, info.RuncCommit.ID != "N/A")
|
||||
assert.Check(t, is.Equal(info.RuncCommit.Expected, "")) //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.49.
|
||||
assert.Check(t, is.Equal(info.RuncCommit.Expected, ""))
|
||||
})
|
||||
|
||||
// Expected commits are omitted in API 1.49, but should still be included in older versions.
|
||||
t.Run("1.48", func(t *testing.T) {
|
||||
apiClient, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion("1.48"))
|
||||
res, body, err := request.Get(ctx, "/v1.48/info", request.JSON)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, res.StatusCode, http.StatusOK)
|
||||
|
||||
buf, err := request.ReadBody(body)
|
||||
assert.NilError(t, err)
|
||||
|
||||
info, err := apiClient.Info(ctx)
|
||||
var info legacyInfo
|
||||
err = json.Unmarshal(buf, &info)
|
||||
assert.NilError(t, err)
|
||||
|
||||
assert.Check(t, info.ContainerdCommit.ID != "N/A")
|
||||
assert.Check(t, is.Equal(info.ContainerdCommit.Expected, info.ContainerdCommit.ID)) //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.49.
|
||||
assert.Check(t, is.Equal(info.ContainerdCommit.Expected, info.ContainerdCommit.ID))
|
||||
|
||||
assert.Check(t, info.InitCommit.ID != "N/A")
|
||||
assert.Check(t, is.Equal(info.InitCommit.Expected, info.InitCommit.ID)) //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.49.
|
||||
assert.Check(t, is.Equal(info.InitCommit.Expected, info.InitCommit.ID))
|
||||
|
||||
assert.Check(t, info.RuncCommit.ID != "N/A")
|
||||
assert.Check(t, is.Equal(info.RuncCommit.Expected, info.RuncCommit.ID)) //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.49.
|
||||
assert.Check(t, is.Equal(info.RuncCommit.Expected, info.RuncCommit.ID))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
3
vendor/github.com/moby/moby/api/types/registry/registry.go
generated
vendored
3
vendor/github.com/moby/moby/api/types/registry/registry.go
generated
vendored
@@ -11,9 +11,6 @@ type ServiceConfig struct {
|
||||
InsecureRegistryCIDRs []netip.Prefix `json:"InsecureRegistryCIDRs"`
|
||||
IndexConfigs map[string]*IndexInfo `json:"IndexConfigs"`
|
||||
Mirrors []string
|
||||
|
||||
// ExtraFields is for internal use to include deprecated fields on older API versions.
|
||||
ExtraFields map[string]any `json:"-"`
|
||||
}
|
||||
|
||||
// IndexInfo contains information about a registry
|
||||
|
||||
5
vendor/github.com/moby/moby/api/types/system/info.go
generated
vendored
5
vendor/github.com/moby/moby/api/types/system/info.go
generated
vendored
@@ -139,11 +139,6 @@ type PluginsInfo struct {
|
||||
type Commit struct {
|
||||
// ID is the actual commit ID or version of external tool.
|
||||
ID string
|
||||
|
||||
// Expected is the commit ID of external tool expected by dockerd as set at build time.
|
||||
//
|
||||
// Deprecated: this field is no longer used in API v1.49, but kept for backward-compatibility with older API versions.
|
||||
Expected string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// NetworkAddressPool is a temp struct used by [Info] struct.
|
||||
|
||||
Reference in New Issue
Block a user