Merge pull request #45759 from akerouanton/validate-ipam-config
Validate IPAM config before handing it over to libnetwork
This commit is contained in:
commit
8383430946
27 changed files with 373 additions and 16 deletions
|
@ -9929,6 +9929,10 @@ paths:
|
|||
example:
|
||||
Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30"
|
||||
Warning: ""
|
||||
400:
|
||||
description: "bad parameter"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
403:
|
||||
description: |
|
||||
Forbidden operation. This happens when trying to create a network named after a pre-defined network,
|
||||
|
|
132
api/types/network/ipam.go
Normal file
132
api/types/network/ipam.go
Normal file
|
@ -0,0 +1,132 @@
|
|||
package network
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
|
||||
"github.com/docker/docker/internal/multierror"
|
||||
)
|
||||
|
||||
// IPAM represents IP Address Management
|
||||
type IPAM struct {
|
||||
Driver string
|
||||
Options map[string]string // Per network IPAM driver options
|
||||
Config []IPAMConfig
|
||||
}
|
||||
|
||||
// IPAMConfig represents IPAM configurations
|
||||
type IPAMConfig struct {
|
||||
Subnet string `json:",omitempty"`
|
||||
IPRange string `json:",omitempty"`
|
||||
Gateway string `json:",omitempty"`
|
||||
AuxAddress map[string]string `json:"AuxiliaryAddresses,omitempty"`
|
||||
}
|
||||
|
||||
type ipFamily string
|
||||
|
||||
const (
|
||||
ip4 ipFamily = "IPv4"
|
||||
ip6 ipFamily = "IPv6"
|
||||
)
|
||||
|
||||
func ValidateIPAM(ipam *IPAM, enableIPv6 bool) error {
|
||||
if ipam == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var errs []error
|
||||
for _, cfg := range ipam.Config {
|
||||
subnet, err := netip.ParsePrefix(cfg.Subnet)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("invalid subnet %s: invalid CIDR block notation", cfg.Subnet))
|
||||
continue
|
||||
}
|
||||
subnetFamily := ip4
|
||||
if subnet.Addr().Is6() {
|
||||
subnetFamily = ip6
|
||||
}
|
||||
|
||||
if subnet != subnet.Masked() {
|
||||
errs = append(errs, fmt.Errorf("invalid subnet %s: it should be %s", subnet, subnet.Masked()))
|
||||
}
|
||||
|
||||
if !enableIPv6 && subnetFamily == ip6 {
|
||||
errs = append(errs, fmt.Errorf("invalid subnet %s: IPv6 has not been enabled for this network", subnet))
|
||||
}
|
||||
|
||||
if ipRangeErrs := validateIPRange(cfg.IPRange, subnet, subnetFamily); len(ipRangeErrs) > 0 {
|
||||
errs = append(errs, ipRangeErrs...)
|
||||
}
|
||||
|
||||
if err := validateAddress(cfg.Gateway, subnet, subnetFamily); err != nil {
|
||||
errs = append(errs, fmt.Errorf("invalid gateway %s: %w", cfg.Gateway, err))
|
||||
}
|
||||
|
||||
for auxName, aux := range cfg.AuxAddress {
|
||||
if err := validateAddress(aux, subnet, subnetFamily); err != nil {
|
||||
errs = append(errs, fmt.Errorf("invalid auxiliary address %s: %w", auxName, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := multierror.Join(errs...); err != nil {
|
||||
return fmt.Errorf("invalid network config:\n%w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateIPRange(ipRange string, subnet netip.Prefix, subnetFamily ipFamily) []error {
|
||||
if ipRange == "" {
|
||||
return nil
|
||||
}
|
||||
prefix, err := netip.ParsePrefix(ipRange)
|
||||
if err != nil {
|
||||
return []error{fmt.Errorf("invalid ip-range %s: invalid CIDR block notation", ipRange)}
|
||||
}
|
||||
family := ip4
|
||||
if prefix.Addr().Is6() {
|
||||
family = ip6
|
||||
}
|
||||
|
||||
if family != subnetFamily {
|
||||
return []error{fmt.Errorf("invalid ip-range %s: parent subnet is an %s block", ipRange, subnetFamily)}
|
||||
}
|
||||
|
||||
var errs []error
|
||||
if prefix.Bits() < subnet.Bits() {
|
||||
errs = append(errs, fmt.Errorf("invalid ip-range %s: CIDR block is bigger than its parent subnet %s", ipRange, subnet))
|
||||
}
|
||||
if prefix != prefix.Masked() {
|
||||
errs = append(errs, fmt.Errorf("invalid ip-range %s: it should be %s", prefix, prefix.Masked()))
|
||||
}
|
||||
if !subnet.Overlaps(prefix) {
|
||||
errs = append(errs, fmt.Errorf("invalid ip-range %s: parent subnet %s doesn't contain ip-range", ipRange, subnet))
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func validateAddress(address string, subnet netip.Prefix, subnetFamily ipFamily) error {
|
||||
if address == "" {
|
||||
return nil
|
||||
}
|
||||
addr, err := netip.ParseAddr(address)
|
||||
if err != nil {
|
||||
return errors.New("invalid address")
|
||||
}
|
||||
family := ip4
|
||||
if addr.Is6() {
|
||||
family = ip6
|
||||
}
|
||||
|
||||
if family != subnetFamily {
|
||||
return fmt.Errorf("parent subnet is an %s block", subnetFamily)
|
||||
}
|
||||
if !subnet.Contains(addr) {
|
||||
return fmt.Errorf("parent subnet %s doesn't contain this address", subnet)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
143
api/types/network/ipam_test.go
Normal file
143
api/types/network/ipam_test.go
Normal file
|
@ -0,0 +1,143 @@
|
|||
package network
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
func TestNetworkWithInvalidIPAM(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
ipam IPAM
|
||||
ipv6 bool
|
||||
expectedErrors []string
|
||||
}{
|
||||
{
|
||||
name: "IP version mismatch",
|
||||
ipam: IPAM{
|
||||
Config: []IPAMConfig{{
|
||||
Subnet: "10.10.10.0/24",
|
||||
IPRange: "2001:db8::/32",
|
||||
Gateway: "2001:db8::1",
|
||||
AuxAddress: map[string]string{"DefaultGatewayIPv4": "2001:db8::1"},
|
||||
}},
|
||||
},
|
||||
expectedErrors: []string{
|
||||
"invalid ip-range 2001:db8::/32: parent subnet is an IPv4 block",
|
||||
"invalid gateway 2001:db8::1: parent subnet is an IPv4 block",
|
||||
"invalid auxiliary address DefaultGatewayIPv4: parent subnet is an IPv4 block",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "IPv6 subnet is discarded when IPv6 is disabled",
|
||||
ipam: IPAM{Config: []IPAMConfig{{Subnet: "2001:db8::/32"}}},
|
||||
ipv6: false,
|
||||
expectedErrors: []string{"invalid subnet 2001:db8::/32: IPv6 has not been enabled for this network"},
|
||||
},
|
||||
{
|
||||
name: "Invalid data - Subnet",
|
||||
ipam: IPAM{Config: []IPAMConfig{{Subnet: "foobar"}}},
|
||||
expectedErrors: []string{
|
||||
`invalid subnet foobar: invalid CIDR block notation`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Invalid data",
|
||||
ipam: IPAM{
|
||||
Config: []IPAMConfig{{
|
||||
Subnet: "10.10.10.0/24",
|
||||
IPRange: "foobar",
|
||||
Gateway: "1001.10.5.3",
|
||||
AuxAddress: map[string]string{"DefaultGatewayIPv4": "dummy"},
|
||||
}},
|
||||
},
|
||||
expectedErrors: []string{
|
||||
"invalid ip-range foobar: invalid CIDR block notation",
|
||||
"invalid gateway 1001.10.5.3: invalid address",
|
||||
"invalid auxiliary address DefaultGatewayIPv4: invalid address",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "IPRange bigger than its subnet",
|
||||
ipam: IPAM{
|
||||
Config: []IPAMConfig{
|
||||
{Subnet: "10.10.10.0/24", IPRange: "10.0.0.0/8"},
|
||||
},
|
||||
},
|
||||
expectedErrors: []string{
|
||||
"invalid ip-range 10.0.0.0/8: CIDR block is bigger than its parent subnet 10.10.10.0/24",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Out of range prefix & addresses",
|
||||
ipam: IPAM{
|
||||
Config: []IPAMConfig{{
|
||||
Subnet: "10.0.0.0/8",
|
||||
IPRange: "192.168.0.1/24",
|
||||
Gateway: "192.168.0.1",
|
||||
AuxAddress: map[string]string{"DefaultGatewayIPv4": "192.168.0.1"},
|
||||
}},
|
||||
},
|
||||
expectedErrors: []string{
|
||||
"invalid ip-range 192.168.0.1/24: it should be 192.168.0.0/24",
|
||||
"invalid ip-range 192.168.0.1/24: parent subnet 10.0.0.0/8 doesn't contain ip-range",
|
||||
"invalid gateway 192.168.0.1: parent subnet 10.0.0.0/8 doesn't contain this address",
|
||||
"invalid auxiliary address DefaultGatewayIPv4: parent subnet 10.0.0.0/8 doesn't contain this address",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Subnet with host fragment set",
|
||||
ipam: IPAM{
|
||||
Config: []IPAMConfig{{
|
||||
Subnet: "10.10.10.0/8",
|
||||
}},
|
||||
},
|
||||
expectedErrors: []string{"invalid subnet 10.10.10.0/8: it should be 10.0.0.0/8"},
|
||||
},
|
||||
{
|
||||
name: "IPRange with host fragment set",
|
||||
ipam: IPAM{
|
||||
Config: []IPAMConfig{{
|
||||
Subnet: "10.0.0.0/8",
|
||||
IPRange: "10.10.10.0/16",
|
||||
}},
|
||||
},
|
||||
expectedErrors: []string{"invalid ip-range 10.10.10.0/16: it should be 10.10.0.0/16"},
|
||||
},
|
||||
{
|
||||
name: "Empty IPAM is valid",
|
||||
ipam: IPAM{},
|
||||
},
|
||||
{
|
||||
name: "Valid IPAM",
|
||||
ipam: IPAM{
|
||||
Config: []IPAMConfig{{
|
||||
Subnet: "10.0.0.0/8",
|
||||
IPRange: "10.10.0.0/16",
|
||||
Gateway: "10.10.0.1",
|
||||
AuxAddress: map[string]string{"DefaultGatewayIPv4": "10.10.0.1"},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
errs := ValidateIPAM(&tc.ipam, tc.ipv6)
|
||||
if tc.expectedErrors == nil {
|
||||
assert.NilError(t, errs)
|
||||
return
|
||||
}
|
||||
|
||||
assert.Check(t, is.ErrorContains(errs, "invalid network config"))
|
||||
for _, expected := range tc.expectedErrors {
|
||||
assert.Check(t, is.ErrorContains(errs, expected))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -9,21 +9,6 @@ type Address struct {
|
|||
PrefixLen int
|
||||
}
|
||||
|
||||
// IPAM represents IP Address Management
|
||||
type IPAM struct {
|
||||
Driver string
|
||||
Options map[string]string // Per network IPAM driver options
|
||||
Config []IPAMConfig
|
||||
}
|
||||
|
||||
// IPAMConfig represents IPAM configurations
|
||||
type IPAMConfig struct {
|
||||
Subnet string `json:",omitempty"`
|
||||
IPRange string `json:",omitempty"`
|
||||
Gateway string `json:",omitempty"`
|
||||
AuxAddress map[string]string `json:"AuxiliaryAddresses,omitempty"`
|
||||
}
|
||||
|
||||
// EndpointIPAMConfig represents IPAM configurations for the endpoint
|
||||
type EndpointIPAMConfig struct {
|
||||
IPv4Address string `json:",omitempty"`
|
||||
|
|
|
@ -346,6 +346,9 @@ func (daemon *Daemon) createNetwork(cfg *config.Config, create types.NetworkCrea
|
|||
nwOptions = append(nwOptions, libnetwork.NetworkOptionConfigOnly())
|
||||
}
|
||||
|
||||
if err := network.ValidateIPAM(create.IPAM, create.EnableIPv6); err != nil {
|
||||
return nil, errdefs.InvalidParameter(err)
|
||||
}
|
||||
if create.IPAM != nil {
|
||||
ipam := create.IPAM
|
||||
v4Conf, v6Conf, err := getIpamConfig(ipam.Config)
|
||||
|
|
|
@ -6149,6 +6149,10 @@ paths:
|
|||
example:
|
||||
Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30"
|
||||
Warning: ""
|
||||
400:
|
||||
description: "bad parameter"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
403:
|
||||
description: "operation not supported for pre-defined networks"
|
||||
schema:
|
||||
|
|
|
@ -6158,6 +6158,10 @@ paths:
|
|||
example:
|
||||
Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30"
|
||||
Warning: ""
|
||||
400:
|
||||
description: "bad parameter"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
403:
|
||||
description: "operation not supported for pre-defined networks"
|
||||
schema:
|
||||
|
|
|
@ -6229,6 +6229,10 @@ paths:
|
|||
example:
|
||||
Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30"
|
||||
Warning: ""
|
||||
400:
|
||||
description: "bad parameter"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
403:
|
||||
description: "operation not supported for pre-defined networks"
|
||||
schema:
|
||||
|
|
|
@ -6358,6 +6358,10 @@ paths:
|
|||
example:
|
||||
Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30"
|
||||
Warning: ""
|
||||
400:
|
||||
description: "bad parameter"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
403:
|
||||
description: "operation not supported for pre-defined networks"
|
||||
schema:
|
||||
|
|
|
@ -6396,6 +6396,10 @@ paths:
|
|||
example:
|
||||
Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30"
|
||||
Warning: ""
|
||||
400:
|
||||
description: "bad parameter"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
403:
|
||||
description: "operation not supported for pre-defined networks"
|
||||
schema:
|
||||
|
|
|
@ -6659,6 +6659,10 @@ paths:
|
|||
example:
|
||||
Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30"
|
||||
Warning: ""
|
||||
400:
|
||||
description: "bad parameter"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
403:
|
||||
description: "operation not supported for pre-defined networks"
|
||||
schema:
|
||||
|
|
|
@ -6757,6 +6757,10 @@ paths:
|
|||
example:
|
||||
Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30"
|
||||
Warning: ""
|
||||
400:
|
||||
description: "bad parameter"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
403:
|
||||
description: "operation not supported for pre-defined networks"
|
||||
schema:
|
||||
|
|
|
@ -7798,6 +7798,10 @@ paths:
|
|||
example:
|
||||
Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30"
|
||||
Warning: ""
|
||||
400:
|
||||
description: "bad parameter"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
403:
|
||||
description: "operation not supported for pre-defined networks"
|
||||
schema:
|
||||
|
|
|
@ -7807,6 +7807,10 @@ paths:
|
|||
example:
|
||||
Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30"
|
||||
Warning: ""
|
||||
400:
|
||||
description: "bad parameter"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
403:
|
||||
description: "operation not supported for pre-defined networks"
|
||||
schema:
|
||||
|
|
|
@ -7848,6 +7848,10 @@ paths:
|
|||
example:
|
||||
Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30"
|
||||
Warning: ""
|
||||
400:
|
||||
description: "bad parameter"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
403:
|
||||
description: "operation not supported for pre-defined networks"
|
||||
schema:
|
||||
|
|
|
@ -7860,6 +7860,10 @@ paths:
|
|||
example:
|
||||
Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30"
|
||||
Warning: ""
|
||||
400:
|
||||
description: "bad parameter"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
403:
|
||||
description: "operation not supported for pre-defined networks"
|
||||
schema:
|
||||
|
|
|
@ -7902,6 +7902,10 @@ paths:
|
|||
example:
|
||||
Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30"
|
||||
Warning: ""
|
||||
400:
|
||||
description: "bad parameter"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
403:
|
||||
description: "operation not supported for pre-defined networks"
|
||||
schema:
|
||||
|
|
|
@ -7945,6 +7945,10 @@ paths:
|
|||
example:
|
||||
Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30"
|
||||
Warning: ""
|
||||
400:
|
||||
description: "bad parameter"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
403:
|
||||
description: "operation not supported for pre-defined networks"
|
||||
schema:
|
||||
|
|
|
@ -8006,6 +8006,10 @@ paths:
|
|||
example:
|
||||
Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30"
|
||||
Warning: ""
|
||||
400:
|
||||
description: "bad parameter"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
403:
|
||||
description: "operation not supported for pre-defined networks"
|
||||
schema:
|
||||
|
|
|
@ -8955,6 +8955,10 @@ paths:
|
|||
example:
|
||||
Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30"
|
||||
Warning: ""
|
||||
400:
|
||||
description: "bad parameter"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
403:
|
||||
description: "operation not supported for pre-defined networks"
|
||||
schema:
|
||||
|
|
|
@ -9292,6 +9292,10 @@ paths:
|
|||
example:
|
||||
Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30"
|
||||
Warning: ""
|
||||
400:
|
||||
description: "bad parameter"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
403:
|
||||
description: "operation not supported for pre-defined networks"
|
||||
schema:
|
||||
|
|
|
@ -9499,6 +9499,10 @@ paths:
|
|||
example:
|
||||
Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30"
|
||||
Warning: ""
|
||||
400:
|
||||
description: "bad parameter"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
403:
|
||||
description: "operation not supported for pre-defined networks"
|
||||
schema:
|
||||
|
|
|
@ -9877,6 +9877,10 @@ paths:
|
|||
example:
|
||||
Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30"
|
||||
Warning: ""
|
||||
400:
|
||||
description: "bad parameter"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
403:
|
||||
description: "operation not supported for pre-defined networks"
|
||||
schema:
|
||||
|
|
|
@ -9895,6 +9895,10 @@ paths:
|
|||
example:
|
||||
Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30"
|
||||
Warning: ""
|
||||
400:
|
||||
description: "bad parameter"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
403:
|
||||
description: "operation not supported for pre-defined networks"
|
||||
schema:
|
||||
|
|
|
@ -38,6 +38,9 @@ keywords: "API, Docker, rcli, REST, documentation"
|
|||
specifications directories. The use of the applied setting requires the daemon
|
||||
to have expermental enabled, and for non-experimental daemons an empty list is
|
||||
always returned.
|
||||
* `POST /networks/create` now returns a 400 if the `IPAMConfig` has invalid
|
||||
values. Note that this change is _unversioned_ and applied to all API
|
||||
versions on daemon that support version 1.44.
|
||||
|
||||
## v1.43 API changes
|
||||
|
||||
|
|
|
@ -23,7 +23,10 @@ source hack/make/.integration-test-helpers
|
|||
# TODO re-enable test_attach_no_stream after https://github.com/docker/docker-py/issues/2513 is resolved
|
||||
# TODO re-enable test_create_with_device_cgroup_rules after https://github.com/docker/docker-py/issues/2939 is resolved
|
||||
# TODO re-enable test_prune_volumes after https://github.com/docker/docker-py/pull/3051 is resolved
|
||||
: "${PY_TEST_OPTIONS:=--junitxml=${DEST}/junit-report.xml --deselect=tests/integration/api_container_test.py::AttachContainerTest::test_attach_no_stream --deselect=tests/integration/api_container_test.py::CreateContainerTest::test_create_with_device_cgroup_rules --deselect=tests/integration/api_volume_test.py::TestVolumes::test_prune_volumes}"
|
||||
# TODO re-enable test_connect_with_ipv6_address after we updated to a version of docker-py with https://github.com/docker/docker-py/pull/3169
|
||||
# TODO re-enable test_create_network_ipv6_enabled after we updated to a version of docker-py with https://github.com/docker/docker-py/pull/3169
|
||||
# TODO re-enable test_create_with_ipv6_address after we updated to a version of docker-py with https://github.com/docker/docker-py/pull/3169
|
||||
: "${PY_TEST_OPTIONS:=--junitxml=${DEST}/junit-report.xml --deselect=tests/integration/api_container_test.py::AttachContainerTest::test_attach_no_stream --deselect=tests/integration/api_container_test.py::CreateContainerTest::test_create_with_device_cgroup_rules --deselect=tests/integration/api_volume_test.py::TestVolumes::test_prune_volumes --deselect=tests/integration/api_network_test.py::TestNetworks::test_connect_with_ipv6_address --deselect=tests/integration/api_network_test.py::TestNetworks::test_create_network_ipv6_enabled --deselect=tests/integration/api_network_test.py::TestNetworks::test_create_with_ipv6_address}"
|
||||
(
|
||||
bundle .integration-daemon-start
|
||||
|
||||
|
|
|
@ -106,6 +106,14 @@ func (a *Allocator) RequestPool(addressSpace, requestedPool, requestedSubPool st
|
|||
}
|
||||
|
||||
k.Subnet, k.ChildSubnet = k.Subnet.Masked(), k.ChildSubnet.Masked()
|
||||
// Prior to https://github.com/moby/moby/pull/44968, libnetwork would happily accept a ChildSubnet with a bigger
|
||||
// mask than its parent subnet. In such case, it was producing IP addresses based on the parent subnet, and the
|
||||
// child subnet was not allocated from the address pool. Following condition take care of restoring this behavior
|
||||
// for networks created before upgrading to v24.0.
|
||||
if k.ChildSubnet.IsValid() && k.ChildSubnet.Bits() < k.Subnet.Bits() {
|
||||
k.ChildSubnet = k.Subnet
|
||||
}
|
||||
|
||||
err = aSpace.allocateSubnet(k.Subnet, k.ChildSubnet)
|
||||
if err != nil {
|
||||
return "", nil, nil, err
|
||||
|
|
Loading…
Add table
Reference in a new issue