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  }