From a23dcf47987949e7883c6d31d54c09d9dda47ca6 Mon Sep 17 00:00:00 2001 From: Rob Murray Date: Tue, 23 Jul 2024 11:43:20 +0100 Subject: [PATCH 1/4] Bump API version to 1.47 Signed-off-by: Rob Murray --- api/common.go | 2 +- api/swagger.yaml | 8 ++++---- docs/api/version-history.md | 6 ++++++ 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/api/common.go b/api/common.go index f831735f840e8..93d64cd8d5ffe 100644 --- a/api/common.go +++ b/api/common.go @@ -3,7 +3,7 @@ package api // import "github.com/docker/docker/api" // Common constants for daemon and client. const ( // DefaultVersion of the current REST API. - DefaultVersion = "1.46" + DefaultVersion = "1.47" // MinSupportedAPIVersion is the minimum API version that can be supported // by the API server, specified as "major.minor". Note that the daemon diff --git a/api/swagger.yaml b/api/swagger.yaml index 802ed8ba3fbc3..bb3f35b8e5403 100644 --- a/api/swagger.yaml +++ b/api/swagger.yaml @@ -19,10 +19,10 @@ produces: consumes: - "application/json" - "text/plain" -basePath: "/v1.46" +basePath: "/v1.47" info: title: "Docker Engine API" - version: "1.46" + version: "1.47" x-logo: url: "https://docs.docker.com/assets/images/logo-docker-main.png" description: | @@ -55,8 +55,8 @@ info: the URL is not supported by the daemon, a HTTP `400 Bad Request` error message is returned. - If you omit the version-prefix, the current version of the API (v1.46) is used. - For example, calling `/info` is the same as calling `/v1.46/info`. Using the + If you omit the version-prefix, the current version of the API (v1.47) is used. + For example, calling `/info` is the same as calling `/v1.47/info`. Using the API without a version-prefix is deprecated and will be removed in a future release. Engine releases in the near future should support this version of the API, diff --git a/docs/api/version-history.md b/docs/api/version-history.md index 3853949a827cf..6f8508fcc428c 100644 --- a/docs/api/version-history.md +++ b/docs/api/version-history.md @@ -13,6 +13,12 @@ keywords: "API, Docker, rcli, REST, documentation" will be rejected. --> +## v1.47 API changes + +[Docker Engine API v1.47](https://docs.docker.com/engine/api/v1.47/) documentation + +* `Sysctls` in `HostConfig` (top level `--sysctl` settings) for `eth0` are no + longer migrated to `DriverOpts`, as described in the changes for v1.46. ## v1.46 API changes From d4d86111642916149b54aa99ef4e48d6665465cc Mon Sep 17 00:00:00 2001 From: Rob Murray Date: Tue, 30 Jul 2024 20:04:45 +0100 Subject: [PATCH 2/4] Added API create/inspect option EnableIPv4 Signed-off-by: Rob Murray --- api/server/router/network/network_routes.go | 7 +++++++ api/swagger.yaml | 14 ++++++++++++++ api/types/network/network.go | 4 +++- docs/api/version-history.md | 5 +++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/api/server/router/network/network_routes.go b/api/server/router/network/network_routes.go index f5e996805a20a..f053468bff335 100644 --- a/api/server/router/network/network_routes.go +++ b/api/server/router/network/network_routes.go @@ -212,6 +212,13 @@ func (n *networkRouter) postNetworkCreate(ctx context.Context, w http.ResponseWr return libnetwork.NetworkNameError(create.Name) } + version := httputils.VersionFromContext(ctx) + + // EnableIPv4 was introduced in API 1.47. + if versions.LessThan(version, "1.47") { + create.EnableIPv4 = nil + } + // For a Swarm-scoped network, this call to backend.CreateNetwork is used to // validate the configuration. The network will not be created but, if the // configuration is valid, ManagerRedirectError will be returned and handled diff --git a/api/swagger.yaml b/api/swagger.yaml index bb3f35b8e5403..91105962179fe 100644 --- a/api/swagger.yaml +++ b/api/swagger.yaml @@ -2484,6 +2484,11 @@ definitions: `overlay`). type: "string" example: "overlay" + EnableIPv4: + description: | + Whether the network was created with IPv4 enabled. + type: "boolean" + example: true EnableIPv6: description: | Whether the network was created with IPv6 enabled. @@ -10377,6 +10382,7 @@ paths: Created: "2016-10-19T06:21:00.416543526Z" Scope: "local" Driver: "bridge" + EnableIPv4: true EnableIPv6: false Internal: false Attachable: false @@ -10398,6 +10404,7 @@ paths: Created: "0001-01-01T00:00:00Z" Scope: "local" Driver: "null" + EnableIPv4: false EnableIPv6: false Internal: false Attachable: false @@ -10412,6 +10419,7 @@ paths: Created: "0001-01-01T00:00:00Z" Scope: "local" Driver: "host" + EnableIPv4: false EnableIPv6: false Internal: false Attachable: false @@ -10597,6 +10605,12 @@ paths: IPAM: description: "Optional custom IP scheme for the network." $ref: "#/definitions/IPAM" + EnableIPv4: + description: | + Enable IPv4 on the network. + To disable IPv4, the daemon must be started with experimental features enabled. + type: "boolean" + example: true EnableIPv6: description: "Enable IPv6 on the network." type: "boolean" diff --git a/api/types/network/network.go b/api/types/network/network.go index c8db97a7e6740..d34b8ab724983 100644 --- a/api/types/network/network.go +++ b/api/types/network/network.go @@ -33,6 +33,7 @@ type CreateRequest struct { type CreateOptions struct { Driver string // Driver is the driver-name used to create the network (e.g. `bridge`, `overlay`) Scope string // Scope describes the level at which the network exists (e.g. `swarm` for cluster-wide or `local` for machine level). + EnableIPv4 *bool `json:",omitempty"` // EnableIPv4 represents whether to enable IPv4. EnableIPv6 *bool `json:",omitempty"` // EnableIPv6 represents whether to enable IPv6. IPAM *IPAM // IPAM is the network's IP Address Management. Internal bool // Internal represents if the network is used internal only. @@ -76,7 +77,8 @@ type Inspect struct { Created time.Time // Created is the time the network created Scope string // Scope describes the level at which the network exists (e.g. `swarm` for cluster-wide or `local` for machine level) Driver string // Driver is the Driver name used to create the network (e.g. `bridge`, `overlay`) - EnableIPv6 bool // EnableIPv6 represents whether to enable IPv6 + EnableIPv4 bool // EnableIPv4 represents whether IPv4 is enabled + EnableIPv6 bool // EnableIPv6 represents whether IPv6 is enabled IPAM IPAM // IPAM is the network's IP Address Management Internal bool // Internal represents if the network is used internal only Attachable bool // Attachable represents if the global scope is manually attachable by regular containers from workers in swarm mode. diff --git a/docs/api/version-history.md b/docs/api/version-history.md index 6f8508fcc428c..faa373271e7d2 100644 --- a/docs/api/version-history.md +++ b/docs/api/version-history.md @@ -19,6 +19,11 @@ keywords: "API, Docker, rcli, REST, documentation" * `Sysctls` in `HostConfig` (top level `--sysctl` settings) for `eth0` are no longer migrated to `DriverOpts`, as described in the changes for v1.46. +* `POST /networks/create` now has an `EnableIPv4` field. Setting it to `false` + disables IPv4 IPAM for the network. It can only be set to `false` if the + daemon has experimental features enabled. +* `GET /networks/{id}` now returns an `EnableIPv4` field showing whether the + network has IPv4 IPAM enabled. ## v1.46 API changes From 903daa4dc480390a0e85163011526a55bde66a1f Mon Sep 17 00:00:00 2001 From: Rob Murray Date: Tue, 30 Jul 2024 20:10:34 +0100 Subject: [PATCH 3/4] Add flag 'enableIPv4' to libnetwork.Network Similar to EnableIPv6: - Set it if EnableIPv4 is specified in a create request. - Otherwise, set it if included in `default-network-opts`. - Apart from in a config-from network, so that it doesn't look like the API request set the field. - Include the new field in Network marshalling/unmarshalling test. Signed-off-by: Rob Murray --- daemon/network.go | 22 ++++++++++++++--- integration/internal/network/ops.go | 8 +++++++ libnetwork/libnetwork_internal_test.go | 3 ++- libnetwork/netlabel/labels.go | 3 +++ libnetwork/network.go | 33 +++++++++++++++++++++++++- 5 files changed, 64 insertions(+), 5 deletions(-) diff --git a/daemon/network.go b/daemon/network.go index 45c159f875159..c50c63bf8ea3c 100644 --- a/daemon/network.go +++ b/daemon/network.go @@ -316,18 +316,33 @@ func (daemon *Daemon) createNetwork(cfg *config.Config, create networktypes.Crea } } + enableIPv4 := create.ConfigFrom == nil + if create.EnableIPv4 != nil { + enableIPv4 = *create.EnableIPv4 + } else if v, ok := networkOptions[netlabel.EnableIPv4]; ok { + var err error + if enableIPv4, err = strconv.ParseBool(v); err != nil { + return nil, errdefs.InvalidParameter(fmt.Errorf("driver-opt %q is not a valid bool", netlabel.EnableIPv4)) + } + } + if !enableIPv4 && !daemon.config().Experimental && create.ConfigFrom == nil { + return nil, errdefs.InvalidParameter( + errors.New("IPv4 can only be disabled if experimental features are enabled"), + ) + } + var enableIPv6 bool if create.EnableIPv6 != nil { enableIPv6 = *create.EnableIPv6 - } else { + } else if v, ok := networkOptions[netlabel.EnableIPv6]; ok { var err error - v, ok := networkOptions[netlabel.EnableIPv6] - if enableIPv6, err = strconv.ParseBool(v); ok && err != nil { + if enableIPv6, err = strconv.ParseBool(v); err != nil { return nil, errdefs.InvalidParameter(fmt.Errorf("driver-opt %q is not a valid bool", netlabel.EnableIPv6)) } } nwOptions := []libnetwork.NetworkOption{ + libnetwork.NetworkOptionEnableIPv4(enableIPv4), libnetwork.NetworkOptionEnableIPv6(enableIPv6), libnetwork.NetworkOptionDriverOpts(networkOptions), libnetwork.NetworkOptionLabels(create.Labels), @@ -620,6 +635,7 @@ func buildNetworkResource(nw *libnetwork.Network) networktypes.Inspect { Created: nw.Created(), Scope: nw.Scope(), Driver: nw.Type(), + EnableIPv4: nw.IPv4Enabled(), EnableIPv6: nw.IPv6Enabled(), IPAM: buildIPAMResources(nw), Internal: nw.Internal(), diff --git a/integration/internal/network/ops.go b/integration/internal/network/ops.go index 49f762ecae02a..b5a071c222da5 100644 --- a/integration/internal/network/ops.go +++ b/integration/internal/network/ops.go @@ -11,6 +11,14 @@ func WithDriver(driver string) func(*network.CreateOptions) { } } +// WithIPv4 enables/disables IPv4 on the network +func WithIPv4(enable bool) func(*network.CreateOptions) { + return func(n *network.CreateOptions) { + enableIPv4 := enable + n.EnableIPv4 = &enableIPv4 + } +} + // WithIPv6 Enables IPv6 on the network func WithIPv6() func(*network.CreateOptions) { return func(n *network.CreateOptions) { diff --git a/libnetwork/libnetwork_internal_test.go b/libnetwork/libnetwork_internal_test.go index 24126a52999bf..bc5bbd294dc1e 100644 --- a/libnetwork/libnetwork_internal_test.go +++ b/libnetwork/libnetwork_internal_test.go @@ -29,6 +29,7 @@ func TestNetworkMarshalling(t *testing.T) { ipamType: "default", addrSpace: "viola", networkType: "bridge", + enableIPv4: true, enableIPv6: true, persist: true, configOnly: true, @@ -138,7 +139,7 @@ func TestNetworkMarshalling(t *testing.T) { } if n.name != nn.name || n.id != nn.id || n.networkType != nn.networkType || n.ipamType != nn.ipamType || - n.addrSpace != nn.addrSpace || n.enableIPv6 != nn.enableIPv6 || + n.addrSpace != nn.addrSpace || n.enableIPv4 != nn.enableIPv4 || n.enableIPv6 != nn.enableIPv6 || n.persist != nn.persist || !compareIpamConfList(n.ipamV4Config, nn.ipamV4Config) || !compareIpamInfoList(n.ipamV4Info, nn.ipamV4Info) || !compareIpamConfList(n.ipamV6Config, nn.ipamV6Config) || !compareIpamInfoList(n.ipamV6Info, nn.ipamV6Info) || diff --git a/libnetwork/netlabel/labels.go b/libnetwork/netlabel/labels.go index 5e243b8520fa6..f7467b68a1da1 100644 --- a/libnetwork/netlabel/labels.go +++ b/libnetwork/netlabel/labels.go @@ -30,6 +30,9 @@ const ( // where the interface name is represented by the string "IFNAME". EndpointSysctls = Prefix + ".endpoint.sysctls" + // EnableIPv4 constant represents enabling IPV4 at network level + EnableIPv4 = Prefix + ".enable_ipv4" + // EnableIPv6 constant represents enabling IPV6 at network level EnableIPv6 = Prefix + ".enable_ipv6" diff --git a/libnetwork/network.go b/libnetwork/network.go index 3b0639553b251..40f68ca2e76ce 100644 --- a/libnetwork/network.go +++ b/libnetwork/network.go @@ -187,6 +187,7 @@ type Network struct { ipamV6Config []*IpamConf ipamV4Info []*IpamInfo ipamV6Info []*IpamInfo + enableIPv4 bool enableIPv6 bool postIPv6 bool epCnt *endpointCnt @@ -368,7 +369,7 @@ func (n *Network) validateConfiguration() error { } if n.ipamType != "" && n.ipamType != defaultIpamForNetworkType(n.networkType) || - n.enableIPv6 || + n.enableIPv4 || n.enableIPv6 || len(n.labels) > 0 || len(n.ipamOptions) > 0 || len(n.ipamV4Config) > 0 || len(n.ipamV6Config) > 0 { return types.ForbiddenErrorf("user specified configurations are not supported if the network depends on a configuration network") @@ -401,6 +402,7 @@ func (n *Network) validateConfiguration() error { // applyConfigurationTo applies network specific configurations. func (n *Network) applyConfigurationTo(to *Network) error { + to.enableIPv4 = n.enableIPv4 to.enableIPv6 = n.enableIPv6 if len(n.labels) > 0 { to.labels = make(map[string]string, len(n.labels)) @@ -450,6 +452,7 @@ func (n *Network) CopyTo(o datastore.KVObject) error { dstN.scope = n.scope dstN.dynamic = n.dynamic dstN.ipamType = n.ipamType + dstN.enableIPv4 = n.enableIPv4 dstN.enableIPv6 = n.enableIPv6 dstN.persist = n.persist dstN.postIPv6 = n.postIPv6 @@ -539,6 +542,7 @@ func (n *Network) MarshalJSON() ([]byte, error) { netMap["ipamType"] = n.ipamType netMap["ipamOptions"] = n.ipamOptions netMap["addrSpace"] = n.addrSpace + netMap["enableIPv4"] = n.enableIPv4 netMap["enableIPv6"] = n.enableIPv6 if n.generic != nil { netMap["generic"] = n.generic @@ -601,6 +605,12 @@ func (n *Network) UnmarshalJSON(b []byte) (err error) { } } n.networkType = netMap["networkType"].(string) + if v, ok := netMap["enableIPv4"]; ok { + n.enableIPv4 = v.(bool) + } else { + // Set enableIPv4 for IPv4 networks created before the option was added. + n.enableIPv4 = len(n.ipamV4Info) > 0 + } n.enableIPv6 = netMap["enableIPv6"].(bool) // if we weren't unmarshaling to netMap we could simply set n.labels @@ -713,6 +723,9 @@ func NetworkOptionGeneric(generic map[string]interface{}) NetworkOption { if n.generic == nil { n.generic = make(map[string]interface{}) } + if val, ok := generic[netlabel.EnableIPv4]; ok { + n.enableIPv4 = val.(bool) + } if val, ok := generic[netlabel.EnableIPv6]; ok { n.enableIPv6 = val.(bool) } @@ -740,6 +753,17 @@ func NetworkOptionPersist(persist bool) NetworkOption { } } +// NetworkOptionEnableIPv4 returns an option setter to explicitly configure IPv4 +func NetworkOptionEnableIPv4(enableIPv4 bool) NetworkOption { + return func(n *Network) { + if n.generic == nil { + n.generic = make(map[string]interface{}) + } + n.enableIPv4 = enableIPv4 + n.generic[netlabel.EnableIPv4] = enableIPv4 + } +} + // NetworkOptionEnableIPv6 returns an option setter to explicitly configure IPv6 func NetworkOptionEnableIPv6(enableIPv6 bool) NetworkOption { return func(n *Network) { @@ -1842,6 +1866,13 @@ func (n *Network) Dynamic() bool { return n.dynamic } +func (n *Network) IPv4Enabled() bool { + n.mu.Lock() + defer n.mu.Unlock() + + return n.enableIPv4 +} + func (n *Network) IPv6Enabled() bool { n.mu.Lock() defer n.mu.Unlock() From 1f542d5d6ce192651deb5871858dfb4bfaec6cf9 Mon Sep 17 00:00:00 2001 From: Rob Murray Date: Tue, 30 Jul 2024 20:17:54 +0100 Subject: [PATCH 4/4] Set EnableIPv4 for predefined networks Signed-off-by: Rob Murray --- daemon/cluster/executor/container/container.go | 2 ++ daemon/daemon_unix.go | 1 + daemon/daemon_windows.go | 2 ++ libnetwork/default_gateway_linux.go | 1 + 4 files changed, 6 insertions(+) diff --git a/daemon/cluster/executor/container/container.go b/daemon/cluster/executor/container/container.go index 736c06f1f8075..4e698d2d22060 100644 --- a/daemon/cluster/executor/container/container.go +++ b/daemon/cluster/executor/container/container.go @@ -626,6 +626,7 @@ func (c *containerConfig) networkCreateRequest(name string) (clustertypes.Networ return clustertypes.NetworkCreateRequest{}, errors.New("container: unknown network referenced") } + ipv4Enabled := true ipv6Enabled := na.Network.Spec.Ipv6Enabled options := network.CreateOptions{ // ID: na.Network.ID, @@ -633,6 +634,7 @@ func (c *containerConfig) networkCreateRequest(name string) (clustertypes.Networ Internal: na.Network.Spec.Internal, Attachable: na.Network.Spec.Attachable, Ingress: convert.IsIngressNetwork(na.Network), + EnableIPv4: &ipv4Enabled, EnableIPv6: &ipv6Enabled, Scope: scope.Swarm, } diff --git a/daemon/daemon_unix.go b/daemon/daemon_unix.go index 475eaee9d7ff1..fc53019d86b90 100644 --- a/daemon/daemon_unix.go +++ b/daemon/daemon_unix.go @@ -1051,6 +1051,7 @@ func initBridgeDriver(controller *libnetwork.Controller, cfg config.BridgeConfig } // Initialize default network on "bridge" with the same name _, err = controller.NewNetwork("bridge", network.NetworkBridge, "", + libnetwork.NetworkOptionEnableIPv4(true), libnetwork.NetworkOptionEnableIPv6(cfg.EnableIPv6), libnetwork.NetworkOptionDriverOpts(netOption), libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf, nil), diff --git a/daemon/daemon_windows.go b/daemon/daemon_windows.go index 9f2c793df90b9..81894cca7368e 100644 --- a/daemon/daemon_windows.go +++ b/daemon/daemon_windows.go @@ -386,6 +386,7 @@ func (daemon *Daemon) initNetworkController(daemonCfg *config.Config, activeSand _, err := daemon.netController.NewNetwork(strings.ToLower(v.Type), name, nid, libnetwork.NetworkOptionGeneric(options.Generic{ netlabel.GenericData: netOption, + netlabel.EnableIPv4: true, }), libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf, nil), ) @@ -430,6 +431,7 @@ func initBridgeDriver(controller *libnetwork.Controller, config config.BridgeCon _, err := controller.NewNetwork(network.DefaultNetwork, network.DefaultNetwork, "", libnetwork.NetworkOptionGeneric(options.Generic{ netlabel.GenericData: netOption, + netlabel.EnableIPv4: true, }), ipamOption, ) diff --git a/libnetwork/default_gateway_linux.go b/libnetwork/default_gateway_linux.go index bbed4e4d0ec03..d4cef393d6866 100644 --- a/libnetwork/default_gateway_linux.go +++ b/libnetwork/default_gateway_linux.go @@ -20,6 +20,7 @@ func (c *Controller) createGWNetwork() (*Network, error) { bridge.EnableICC: strconv.FormatBool(false), bridge.EnableIPMasquerade: strconv.FormatBool(true), }), + NetworkOptionEnableIPv4(true), NetworkOptionEnableIPv6(false), ) if err != nil {