github.com/moby/docker@v26.1.3+incompatible/integration/networking/mac_addr_test.go (about) 1 package networking 2 3 import ( 4 "testing" 5 6 containertypes "github.com/docker/docker/api/types/container" 7 "github.com/docker/docker/client" 8 "github.com/docker/docker/integration/internal/container" 9 "github.com/docker/docker/integration/internal/network" 10 "github.com/docker/docker/libnetwork/drivers/bridge" 11 "github.com/docker/docker/testutil" 12 "github.com/docker/docker/testutil/daemon" 13 "gotest.tools/v3/assert" 14 is "gotest.tools/v3/assert/cmp" 15 "gotest.tools/v3/skip" 16 ) 17 18 // TestMACAddrOnRestart is a regression test for https://github.com/moby/moby/issues/47146 19 // - Start a container, let it use a generated MAC address. 20 // - Stop that container. 21 // - Start a second container, it'll also use a generated MAC address. 22 // (It's likely to recycle the first container's MAC address.) 23 // - Restart the first container. 24 // (The bug was that it kept its original MAC address, now already in-use.) 25 // - Check that the two containers have different MAC addresses. 26 func TestMACAddrOnRestart(t *testing.T) { 27 skip.If(t, testEnv.DaemonInfo.OSType == "windows") 28 29 ctx := setupTest(t) 30 31 d := daemon.New(t) 32 d.StartWithBusybox(ctx, t) 33 defer d.Stop(t) 34 35 c := d.NewClientT(t) 36 defer c.Close() 37 38 const netName = "testmacaddrs" 39 network.CreateNoError(ctx, t, c, netName, 40 network.WithDriver("bridge"), 41 network.WithOption(bridge.BridgeName, netName)) 42 defer network.RemoveNoError(ctx, t, c, netName) 43 44 const ctr1Name = "ctr1" 45 id1 := container.Run(ctx, t, c, 46 container.WithName(ctr1Name), 47 container.WithImage("busybox:latest"), 48 container.WithCmd("top"), 49 container.WithNetworkMode(netName)) 50 defer c.ContainerRemove(ctx, id1, containertypes.RemoveOptions{ 51 Force: true, 52 }) 53 err := c.ContainerStop(ctx, ctr1Name, containertypes.StopOptions{}) 54 assert.Assert(t, is.Nil(err)) 55 56 // Start a second container, giving the daemon a chance to recycle the first container's 57 // IP and MAC addresses. 58 const ctr2Name = "ctr2" 59 id2 := container.Run(ctx, t, c, 60 container.WithName(ctr2Name), 61 container.WithImage("busybox:latest"), 62 container.WithCmd("top"), 63 container.WithNetworkMode(netName)) 64 defer c.ContainerRemove(ctx, id2, containertypes.RemoveOptions{ 65 Force: true, 66 }) 67 68 // Restart the first container. 69 err = c.ContainerStart(ctx, ctr1Name, containertypes.StartOptions{}) 70 assert.Assert(t, is.Nil(err)) 71 72 // Check that the containers ended up with different MAC addresses. 73 74 ctr1Inspect := container.Inspect(ctx, t, c, ctr1Name) 75 ctr1MAC := ctr1Inspect.NetworkSettings.Networks[netName].MacAddress 76 77 ctr2Inspect := container.Inspect(ctx, t, c, ctr2Name) 78 ctr2MAC := ctr2Inspect.NetworkSettings.Networks[netName].MacAddress 79 80 assert.Check(t, ctr1MAC != ctr2MAC, 81 "expected containers to have different MAC addresses; got %q for both", ctr1MAC) 82 } 83 84 // Check that a configured MAC address is restored after a container restart, 85 // and after a daemon restart. 86 func TestCfgdMACAddrOnRestart(t *testing.T) { 87 skip.If(t, testEnv.DaemonInfo.OSType == "windows") 88 89 ctx := setupTest(t) 90 91 d := daemon.New(t) 92 d.StartWithBusybox(ctx, t) 93 defer d.Stop(t) 94 95 c := d.NewClientT(t) 96 defer c.Close() 97 98 const netName = "testcfgmacaddr" 99 network.CreateNoError(ctx, t, c, netName, 100 network.WithDriver("bridge"), 101 network.WithOption(bridge.BridgeName, netName)) 102 defer network.RemoveNoError(ctx, t, c, netName) 103 104 const wantMAC = "02:42:ac:11:00:42" 105 const ctr1Name = "ctr1" 106 id1 := container.Run(ctx, t, c, 107 container.WithName(ctr1Name), 108 container.WithImage("busybox:latest"), 109 container.WithCmd("top"), 110 container.WithNetworkMode(netName), 111 container.WithMacAddress(netName, wantMAC)) 112 defer c.ContainerRemove(ctx, id1, containertypes.RemoveOptions{ 113 Force: true, 114 }) 115 116 inspect := container.Inspect(ctx, t, c, ctr1Name) 117 gotMAC := inspect.NetworkSettings.Networks[netName].MacAddress 118 assert.Check(t, is.Equal(wantMAC, gotMAC)) 119 120 startAndCheck := func() { 121 t.Helper() 122 err := c.ContainerStart(ctx, ctr1Name, containertypes.StartOptions{}) 123 assert.Assert(t, is.Nil(err)) 124 inspect = container.Inspect(ctx, t, c, ctr1Name) 125 gotMAC = inspect.NetworkSettings.Networks[netName].MacAddress 126 assert.Check(t, is.Equal(wantMAC, gotMAC)) 127 } 128 129 // Restart the container, check that the MAC address is restored. 130 err := c.ContainerStop(ctx, ctr1Name, containertypes.StopOptions{}) 131 assert.Assert(t, is.Nil(err)) 132 startAndCheck() 133 134 // Restart the daemon, check that the MAC address is restored. 135 err = c.ContainerStop(ctx, ctr1Name, containertypes.StopOptions{}) 136 assert.Assert(t, is.Nil(err)) 137 d.Restart(t) 138 startAndCheck() 139 } 140 141 // Regression test for https://github.com/moby/moby/issues/47228 - check that a 142 // generated MAC address is not included in the Config section of 'inspect' 143 // output, but a configured address is. 144 func TestInspectCfgdMAC(t *testing.T) { 145 skip.If(t, testEnv.DaemonInfo.OSType == "windows") 146 147 ctx := setupTest(t) 148 149 d := daemon.New(t) 150 d.StartWithBusybox(ctx, t) 151 defer d.Stop(t) 152 153 testcases := []struct { 154 name string 155 desiredMAC string 156 netName string 157 ctrWide bool 158 }{ 159 { 160 name: "generated address default bridge", 161 netName: "bridge", 162 }, 163 { 164 name: "configured address default bridge", 165 desiredMAC: "02:42:ac:11:00:42", 166 netName: "bridge", 167 }, 168 { 169 name: "generated address custom bridge", 170 netName: "testnet", 171 }, 172 { 173 name: "configured address custom bridge", 174 desiredMAC: "02:42:ac:11:00:42", 175 netName: "testnet", 176 }, 177 { 178 name: "ctr-wide address default bridge", 179 desiredMAC: "02:42:ac:11:00:42", 180 netName: "bridge", 181 ctrWide: true, 182 }, 183 } 184 185 for _, tc := range testcases { 186 t.Run(tc.name, func(t *testing.T) { 187 ctx := testutil.StartSpan(ctx, t) 188 189 var copts []client.Opt 190 if tc.ctrWide { 191 copts = append(copts, client.WithVersion("1.43")) 192 } 193 c := d.NewClientT(t, copts...) 194 defer c.Close() 195 196 if tc.netName != "bridge" { 197 const netName = "inspectcfgmac" 198 network.CreateNoError(ctx, t, c, netName, 199 network.WithDriver("bridge"), 200 network.WithOption(bridge.BridgeName, netName)) 201 defer network.RemoveNoError(ctx, t, c, netName) 202 } 203 204 const ctrName = "ctr" 205 opts := []func(*container.TestContainerConfig){ 206 container.WithName(ctrName), 207 container.WithCmd("top"), 208 container.WithImage("busybox:latest"), 209 } 210 // Don't specify the network name for the bridge network, because that 211 // exercises a different code path (the network name isn't set until the 212 // container starts, until then it's "default"). 213 if tc.netName != "bridge" { 214 opts = append(opts, container.WithNetworkMode(tc.netName)) 215 } 216 if tc.desiredMAC != "" { 217 if tc.ctrWide { 218 opts = append(opts, container.WithContainerWideMacAddress(tc.desiredMAC)) 219 } else { 220 opts = append(opts, container.WithMacAddress(tc.netName, tc.desiredMAC)) 221 } 222 } 223 id := container.Create(ctx, t, c, opts...) 224 defer c.ContainerRemove(ctx, id, containertypes.RemoveOptions{ 225 Force: true, 226 }) 227 228 inspect := container.Inspect(ctx, t, c, ctrName) 229 configMAC := inspect.Config.MacAddress //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.44. 230 assert.Check(t, is.DeepEqual(configMAC, tc.desiredMAC)) 231 }) 232 } 233 } 234 235 // Regression test for https://github.com/moby/moby/issues/47441 236 // Migration of a container-wide MAC address to the new per-endpoint setting, 237 // where NetworkMode uses network id, and the key in endpoint settings is the 238 // network name. 239 func TestWatchtowerCreate(t *testing.T) { 240 skip.If(t, testEnv.DaemonInfo.OSType == "windows", "no macvlan") 241 242 ctx := setupTest(t) 243 244 d := daemon.New(t) 245 d.StartWithBusybox(ctx, t) 246 defer d.Stop(t) 247 248 c := d.NewClientT(t, client.WithVersion("1.25")) 249 defer c.Close() 250 251 // Create a "/29" network, with a single address in iprange for IPAM to 252 // allocate, but no gateway address. So, the gateway will get the single 253 // free address. It'll only be possible to start a container by explicitly 254 // assigning an address. 255 const netName = "wtmvl" 256 netId := network.CreateNoError(ctx, t, c, netName, 257 network.WithIPAMRange("172.30.0.0/29", "172.30.0.1/32", ""), 258 network.WithDriver("macvlan"), 259 ) 260 defer network.RemoveNoError(ctx, t, c, netName) 261 262 // Start a container, using the network's id in NetworkMode but its name 263 // in EndpointsConfig. (The container-wide MAC address must be merged with 264 // the endpoint config containing the preferred IP address, but the names 265 // don't match.) 266 const ctrName = "ctr1" 267 const ctrIP = "172.30.0.2" 268 const ctrMAC = "02:42:ac:11:00:42" 269 id := container.Run(ctx, t, c, 270 container.WithName(ctrName), 271 container.WithNetworkMode(netId), 272 container.WithContainerWideMacAddress(ctrMAC), 273 container.WithIPv4(netName, ctrIP), 274 ) 275 defer c.ContainerRemove(ctx, id, containertypes.RemoveOptions{Force: true}) 276 277 // Check that the container got the expected addresses. 278 inspect := container.Inspect(ctx, t, c, ctrName) 279 netSettings := inspect.NetworkSettings.Networks[netName] 280 assert.Check(t, is.Equal(netSettings.IPAddress, ctrIP)) 281 assert.Check(t, is.Equal(netSettings.MacAddress, ctrMAC)) 282 }