API: /info: remove BridgeNfIptables, BridgeNfIp6tables fields

The `BridgeNfIptables` and `BridgeNfIp6tables` fields in the
`GET /info` response were deprecated in API v1.48, and are now omitted
in API v1.50.

With this patch, old API version continue to return the field:

    curl -s --unix-socket /var/run/docker.sock http://localhost/v1.48/info | jq .BridgeNfIp6tables
    false

    curl -s --unix-socket /var/run/docker.sock http://localhost/v1.48/info | jq .BridgeNfIptables
    false

Omitting the field in API v1.50 and above

    curl -s --unix-socket /var/run/docker.sock http://localhost/v1.50/info | jq .BridgeNfIp6tables
    null

    curl -s --unix-socket /var/run/docker.sock http://localhost/v1.50/info | jq .BridgeNfIptables
    null

This reverts commit eacbbdeec6, and re-applies
a variant of 5d2006256f

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn
2025-04-30 15:44:08 +02:00
parent 81116f75de
commit 6505d3877c
8 changed files with 144 additions and 16 deletions

View File

@@ -0,0 +1,39 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.23
package system
import (
"encoding/json"
"github.com/docker/docker/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 (sc *infoResponse) MarshalJSON() ([]byte, error) {
type tmp *system.Info
base, err := json.Marshal((tmp)(sc.Info))
if err != nil {
return nil, err
}
if len(sc.extraFields) == 0 {
return base, nil
}
var merged map[string]any
_ = json.Unmarshal(base, &merged)
for k, v := range sc.extraFields {
merged[k] = v
}
return json.Marshal(merged)
}

View File

@@ -0,0 +1,33 @@
package system
import (
"encoding/json"
"strings"
"testing"
"github.com/docker/docker/api/types/system"
)
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))
}
}

View File

@@ -5,7 +5,6 @@ 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/system"
"resenje.org/singleflight"
)
@@ -21,7 +20,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, *system.Info]
collectSystemInfo singleflight.Group[string, *infoResponse]
}
// NewRouter initializes a new system router

View File

@@ -63,7 +63,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) (*system.Info, error) {
info, _, _ := s.collectSystemInfo.Do(ctx, version, func(ctx context.Context) (*infoResponse, error) {
info, err := s.backend.SystemInfo(ctx)
if err != nil {
return nil, err
@@ -117,6 +117,7 @@ func (s *systemRouter) getInfo(ctx context.Context, w http.ResponseWriter, r *ht
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.
@@ -129,9 +130,17 @@ func (s *systemRouter) getInfo(ctx context.Context, w http.ResponseWriter, r *ht
}
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{
"BridgeNfIptables": json.RawMessage("false"),
"BridgeNfIp6tables": json.RawMessage("false"),
}
}
return info, nil
return &infoResponse{Info: info, extraFields: extraFields}, nil
})
return httputils.WriteJSON(w, http.StatusOK, info)
}

View File

@@ -29,8 +29,6 @@ type Info struct {
CPUSet bool
PidsLimit bool
IPv4Forwarding bool
BridgeNfIptables bool `json:"BridgeNfIptables"` // Deprecated: netfilter module is now loaded on-demand and no longer during daemon startup, making this field obsolete. This field is always false and will be removed in the next release.
BridgeNfIP6tables bool `json:"BridgeNfIp6tables"` // Deprecated: netfilter module is now loaded on-demand and no longer during daemon startup, making this field obsolete. This field is always false and will be removed in the next release.
Debug bool
NFd int
OomKillDisable bool

View File

@@ -21,6 +21,9 @@ keywords: "API, Docker, rcli, REST, documentation"
`DeviceInfo` objects, each providing details about a device discovered by a
device driver.
Currently only the CDI device driver is supported.
* Deprecated: The `BridgeNfIptables` and `BridgeNfIp6tables` fields in the
`GET /info` response were deprecated in API v1.48, and are now omitted
in API v1.50.
## v1.49 API changes

View File

@@ -3,9 +3,13 @@
package system // import "github.com/docker/docker/integration/system"
import (
"encoding/json"
"io"
"net/http"
"testing"
"github.com/docker/docker/client"
"github.com/docker/docker/testutil/request"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
@@ -47,3 +51,56 @@ func TestInfoBinaryCommits(t *testing.T) {
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.
})
}
func TestInfoLegacyFields(t *testing.T) {
ctx := setupTest(t)
const notPresent = "expected field to not be present"
tests := []struct {
name string
url string
expectedFields map[string]any
}{
{
name: "api v1.49 legacy bridge-nftables",
url: "/v1.49/info",
expectedFields: map[string]any{
"BridgeNfIp6tables": false,
"BridgeNfIptables": false,
},
},
{
name: "api v1.50 legacy bridge-nftables",
url: "/v1.50/info",
expectedFields: map[string]any{
"BridgeNfIp6tables": notPresent,
"BridgeNfIptables": notPresent,
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
res, _, err := request.Get(ctx, tc.url)
assert.NilError(t, err)
assert.Equal(t, res.StatusCode, http.StatusOK)
body, err := io.ReadAll(res.Body)
assert.NilError(t, err)
actual := map[string]any{}
err = json.Unmarshal(body, &actual)
assert.NilError(t, err, string(body))
for field, expectedValue := range tc.expectedFields {
if expectedValue == notPresent {
_, found := actual[field]
assert.Assert(t, !found, "field %s should not be present", field)
} else {
_, found := actual[field]
assert.Assert(t, found, "field %s should be present", field)
assert.Check(t, is.DeepEqual(actual[field], expectedValue))
}
}
})
}
}

View File

@@ -24,16 +24,6 @@ type SysInfo struct {
// Whether IPv4 forwarding is supported or not, if this was disabled, networking will not work
IPv4ForwardingDisabled bool
// Whether bridge-nf-call-iptables is supported or not
//
// Deprecated: netfilter module is now loaded on-demand and no longer during daemon startup, making this field obsolete. This field is always false and will be removed in the next release.
BridgeNFCallIPTablesDisabled bool
// Whether bridge-nf-call-ip6tables is supported or not
//
// Deprecated: netfilter module is now loaded on-demand and no longer during daemon startup, making this field obsolete. This field is always false and will be removed in the next release.
BridgeNFCallIP6TablesDisabled bool
// Whether the cgroup has the mountpoint of "devices" or not
CgroupDevicesEnabled bool