Skip to content

Commit 2fa6029

Browse files
committed
network/connect: Support MacAddress
Signed-off-by: Paweł Gronowski <[email protected]>
1 parent c0625f2 commit 2fa6029

4 files changed

Lines changed: 62 additions & 3 deletions

File tree

api/docs/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ keywords: "API, Docker, rcli, REST, documentation"
1818
* `GET /images/json` now supports an `identity` query parameter. When set,
1919
the response includes manifest summaries and may include an `Identity` field
2020
for each manifest with trusted identity and origin information.
21+
* `POST /networks/{id}/connect` now correctly applies the `MacAddress` field in
22+
`EndpointSettings`. This field was added in API v1.44, but was previously ignored.
2123

2224
## v1.53 API changes
2325

daemon/container_operations.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,8 @@ func (daemon *Daemon) updateNetworkSettings(ctr *container.Container, n *libnetw
191191
}
192192

193193
ctr.NetworkSettings.Networks[n.Name()] = &network.EndpointSettings{
194-
EndpointSettings: endpointConfig,
194+
EndpointSettings: endpointConfig,
195+
DesiredMacAddress: endpointConfig.MacAddress,
195196
}
196197

197198
return nil
@@ -1046,12 +1047,14 @@ func (daemon *Daemon) ConnectToNetwork(ctx context.Context, ctr *container.Conta
10461047
}
10471048
} else {
10481049
ctr.NetworkSettings.Networks[idOrName] = &network.EndpointSettings{
1049-
EndpointSettings: endpointConfig,
1050+
EndpointSettings: endpointConfig,
1051+
DesiredMacAddress: endpointConfig.MacAddress,
10501052
}
10511053
}
10521054
} else {
10531055
epc := &network.EndpointSettings{
1054-
EndpointSettings: endpointConfig,
1056+
EndpointSettings: endpointConfig,
1057+
DesiredMacAddress: endpointConfig.MacAddress,
10551058
}
10561059
if err := daemon.connectToNetwork(ctx, &daemon.config().Config, ctr, idOrName, epc); err != nil {
10571060
return err

daemon/server/router/network/network_routes.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,11 @@ func (n *networkRouter) postNetworkConnect(ctx context.Context, w http.ResponseW
292292
return err
293293
}
294294

295+
// TODO: Do we want to apply it to the older APIs too?
296+
if versions.LessThan(httputils.VersionFromContext(ctx), "1.54") && connect.EndpointConfig != nil {
297+
connect.EndpointConfig.MacAddress = nil
298+
}
299+
295300
// Unlike other operations, we does not check ambiguity of the name/ID here.
296301
// The reason is that, In case of attachable network in swarm scope, the actual local network
297302
// may not be available at the time. At the same time, inside daemon `ConnectContainerToNetwork`

integration/networking/mac_addr_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"testing"
1010

1111
containertypes "github.com/moby/moby/api/types/container"
12+
networktypes "github.com/moby/moby/api/types/network"
1213
"github.com/moby/moby/client"
1314
"github.com/moby/moby/v2/daemon/libnetwork/drivers/bridge"
1415
"github.com/moby/moby/v2/integration/internal/container"
@@ -305,6 +306,54 @@ func TestWatchtowerCreate(t *testing.T) {
305306
assert.Check(t, is.Equal(netSettings.MacAddress.String(), ctrMAC))
306307
}
307308

309+
// TestNetworkConnectWithMACAddress tests that a MAC address can be specified
310+
// when connecting a container to a network using NetworkConnect.
311+
// This is a feature request from https://github.com/moby/moby/issues/51796
312+
func TestNetworkConnectWithMACAddress(t *testing.T) {
313+
skip.If(t, testEnv.DaemonInfo.OSType == "windows")
314+
315+
ctx := setupTest(t)
316+
317+
d := daemon.New(t)
318+
d.StartWithBusybox(ctx, t)
319+
defer d.Stop(t)
320+
321+
c := d.NewClientT(t)
322+
defer c.Close()
323+
324+
const netName = "testmacconnect"
325+
network.CreateNoError(ctx, t, c, netName,
326+
network.WithDriver("bridge"),
327+
network.WithOption(bridge.BridgeName, netName))
328+
defer network.RemoveNoError(ctx, t, c, netName)
329+
330+
const ctrName = "ctr1"
331+
id := container.Run(ctx, t, c,
332+
container.WithName(ctrName),
333+
container.WithImage("busybox:latest"),
334+
container.WithCmd("top"))
335+
defer c.ContainerRemove(ctx, id, client.ContainerRemoveOptions{Force: true})
336+
337+
const wantMAC = "02:42:ac:11:00:42"
338+
_, err := c.NetworkConnect(ctx, netName, client.NetworkConnectOptions{
339+
Container: ctrName,
340+
EndpointConfig: &networktypes.EndpointSettings{
341+
MacAddress: networktypes.HardwareAddr{0x02, 0x42, 0xac, 0x11, 0x00, 0x42},
342+
},
343+
})
344+
assert.NilError(t, err)
345+
346+
// Verify via container inspect
347+
inspect := container.Inspect(ctx, t, c, ctrName)
348+
gotMAC := inspect.NetworkSettings.Networks[netName].MacAddress
349+
assert.Check(t, is.Equal(wantMAC, gotMAC.String()))
350+
351+
// Verify the MAC address is actually applied to the interface inside the container
352+
res := container.ExecT(ctx, t, c, id, []string{"cat", "/sys/class/net/eth1/address"})
353+
res.AssertSuccess(t)
354+
assert.Check(t, is.Equal(wantMAC+"\n", res.Stdout()))
355+
}
356+
308357
type legacyCreateRequest struct {
309358
containertypes.CreateRequest
310359
// Mac Address of the container.

0 commit comments

Comments
 (0)