diff --git a/integration/container/run_linux_test.go b/integration/container/run_linux_test.go index 2541c6fc74..810ede184b 100644 --- a/integration/container/run_linux_test.go +++ b/integration/container/run_linux_test.go @@ -289,3 +289,40 @@ func TestMacAddressIsAppliedToMainNetworkWithShortID(t *testing.T) { c := container.Inspect(ctx, t, apiClient, cid) assert.Equal(t, c.NetworkSettings.Networks["testnet"].MacAddress, "02:42:08:26:a9:55") } + +func TestStaticIPOutsideSubpool(t *testing.T) { + skip.If(t, testEnv.IsRemoteDaemon) + skip.If(t, testEnv.DaemonInfo.OSType != "linux") + + ctx := testutil.StartSpan(baseContext, t) + + d := daemon.New(t) + d.StartWithBusybox(ctx, t) + defer d.Stop(t) + + apiClient, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion("1.43")) + assert.NilError(t, err) + + const netname = "subnet-range" + n := net.CreateNoError(ctx, t, apiClient, netname, net.WithIPAMRange("10.42.0.0/16", "10.42.128.0/24", "10.42.0.1")) + defer net.RemoveNoError(ctx, t, apiClient, n) + + cID := container.Run(ctx, t, apiClient, + container.WithImage("busybox:latest"), + container.WithCmd("sh", "-c", `ip -4 -oneline addr show eth0`), + container.WithNetworkMode(netname), + container.WithIPv4(netname, "10.42.1.3"), + ) + + poll.WaitOn(t, container.IsStopped(ctx, apiClient, cID), poll.WithDelay(100*time.Millisecond)) + + out, err := apiClient.ContainerLogs(ctx, cID, containertypes.LogsOptions{ShowStdout: true}) + assert.NilError(t, err) + defer out.Close() + + var b bytes.Buffer + _, err = io.Copy(&b, out) + assert.NilError(t, err) + + assert.Check(t, is.Contains(b.String(), "inet 10.42.1.3/16")) +} diff --git a/integration/internal/network/ops.go b/integration/internal/network/ops.go index dc0fce06ef..d7ba6e3464 100644 --- a/integration/internal/network/ops.go +++ b/integration/internal/network/ops.go @@ -73,6 +73,11 @@ func WithOption(key, value string) func(*types.NetworkCreate) { // WithIPAM adds an IPAM with the specified Subnet and Gateway to the network func WithIPAM(subnet, gateway string) func(*types.NetworkCreate) { + return WithIPAMRange(subnet, "", gateway) +} + +// WithIPAM adds an IPAM with the specified Subnet, IPRange and Gateway to the network +func WithIPAMRange(subnet, iprange, gateway string) func(*types.NetworkCreate) { return func(n *types.NetworkCreate) { if n.IPAM == nil { n.IPAM = &network.IPAM{} @@ -80,6 +85,7 @@ func WithIPAM(subnet, gateway string) func(*types.NetworkCreate) { n.IPAM.Config = append(n.IPAM.Config, network.IPAMConfig{ Subnet: subnet, + IPRange: iprange, Gateway: gateway, AuxAddress: map[string]string{}, }) diff --git a/libnetwork/network.go b/libnetwork/network.go index a4aaf2f90b..e3434ca29d 100644 --- a/libnetwork/network.go +++ b/libnetwork/network.go @@ -83,7 +83,7 @@ type IpamConf struct { // PreferredPool is the master address pool for containers and network interfaces. PreferredPool string // SubPool is a subset of the master pool. If specified, - // this becomes the container pool. + // this becomes the container pool for automatic address allocations. SubPool string // Gateway is the preferred Network Gateway address (optional). Gateway string @@ -100,7 +100,7 @@ func (c *IpamConf) Validate() error { return nil } -// Contains checks whether the ipamSubnet contains [addr]. +// Contains checks whether the ipam master address pool contains [addr]. func (c *IpamConf) Contains(addr net.IP) bool { if c == nil { return false @@ -110,9 +110,6 @@ func (c *IpamConf) Contains(addr net.IP) bool { } _, allowedRange, _ := net.ParseCIDR(c.PreferredPool) - if c.SubPool != "" { - _, allowedRange, _ = net.ParseCIDR(c.SubPool) - } return allowedRange.Contains(addr) }