Browse code

No inspect 'Config.MacAddress' unless configured.

Do not set 'Config.MacAddress' in inspect output unless the MAC address
is configured.

Also, make sure it is filled in for a configured address on the default
network before the container is started (by translating the network name
from 'default' to 'config' so that the address lookup works).

Signed-off-by: Rob Murray <rob.murray@docker.com>

Rob Murray authored on 2024/01/30 03:38:05
Showing 2 changed files
... ...
@@ -70,7 +70,6 @@ func (daemon *Daemon) ContainerInspectCurrent(ctx context.Context, name string,
70 70
 		if epConf.EndpointSettings != nil {
71 71
 			// We must make a copy of this pointer object otherwise it can race with other operations
72 72
 			apiNetworks[nwName] = epConf.EndpointSettings.Copy()
73
-			apiNetworks[nwName].DesiredMacAddress = ""
74 73
 		}
75 74
 	}
76 75
 
... ...
@@ -163,8 +162,12 @@ func (daemon *Daemon) getInspectData(daemonCfg *config.Config, container *contai
163 163
 	// unversioned API endpoints.
164 164
 	if container.Config != nil && container.Config.MacAddress == "" { //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.44.
165 165
 		if nwm := hostConfig.NetworkMode; nwm.IsDefault() || nwm.IsBridge() || nwm.IsUserDefined() {
166
-			if epConf, ok := container.NetworkSettings.Networks[nwm.NetworkName()]; ok {
167
-				container.Config.MacAddress = epConf.MacAddress //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.44.
166
+			name := nwm.NetworkName()
167
+			if nwm.IsDefault() {
168
+				name = daemon.netController.Config().DefaultNetwork
169
+			}
170
+			if epConf, ok := container.NetworkSettings.Networks[name]; ok {
171
+				container.Config.MacAddress = epConf.DesiredMacAddress //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.44.
168 172
 			}
169 173
 		}
170 174
 	}
... ...
@@ -4,9 +4,11 @@ import (
4 4
 	"testing"
5 5
 
6 6
 	containertypes "github.com/docker/docker/api/types/container"
7
+	"github.com/docker/docker/client"
7 8
 	"github.com/docker/docker/integration/internal/container"
8 9
 	"github.com/docker/docker/integration/internal/network"
9 10
 	"github.com/docker/docker/libnetwork/drivers/bridge"
11
+	"github.com/docker/docker/testutil"
10 12
 	"github.com/docker/docker/testutil/daemon"
11 13
 	"gotest.tools/v3/assert"
12 14
 	is "gotest.tools/v3/assert/cmp"
... ...
@@ -135,3 +137,97 @@ func TestCfgdMACAddrOnRestart(t *testing.T) {
135 135
 	d.Restart(t)
136 136
 	startAndCheck()
137 137
 }
138
+
139
+// Regression test for https://github.com/moby/moby/issues/47228 - check that a
140
+// generated MAC address is not included in the Config section of 'inspect'
141
+// output, but a configured address is.
142
+func TestInspectCfgdMAC(t *testing.T) {
143
+	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
144
+
145
+	ctx := setupTest(t)
146
+
147
+	d := daemon.New(t)
148
+	d.StartWithBusybox(ctx, t)
149
+	defer d.Stop(t)
150
+
151
+	testcases := []struct {
152
+		name       string
153
+		desiredMAC string
154
+		netName    string
155
+		ctrWide    bool
156
+	}{
157
+		{
158
+			name:    "generated address default bridge",
159
+			netName: "bridge",
160
+		},
161
+		{
162
+			name:       "configured address default bridge",
163
+			desiredMAC: "02:42:ac:11:00:42",
164
+			netName:    "bridge",
165
+		},
166
+		{
167
+			name:    "generated address custom bridge",
168
+			netName: "testnet",
169
+		},
170
+		{
171
+			name:       "configured address custom bridge",
172
+			desiredMAC: "02:42:ac:11:00:42",
173
+			netName:    "testnet",
174
+		},
175
+		{
176
+			name:       "ctr-wide address default bridge",
177
+			desiredMAC: "02:42:ac:11:00:42",
178
+			netName:    "bridge",
179
+			ctrWide:    true,
180
+		},
181
+	}
182
+
183
+	for _, tc := range testcases {
184
+		t.Run(tc.name, func(t *testing.T) {
185
+			ctx := testutil.StartSpan(ctx, t)
186
+
187
+			var copts []client.Opt
188
+			if tc.ctrWide {
189
+				copts = append(copts, client.WithVersion("1.43"))
190
+			}
191
+			c := d.NewClientT(t, copts...)
192
+			defer c.Close()
193
+
194
+			if tc.netName != "bridge" {
195
+				const netName = "inspectcfgmac"
196
+				network.CreateNoError(ctx, t, c, netName,
197
+					network.WithDriver("bridge"),
198
+					network.WithOption(bridge.BridgeName, netName))
199
+				defer network.RemoveNoError(ctx, t, c, netName)
200
+			}
201
+
202
+			const ctrName = "ctr"
203
+			opts := []func(*container.TestContainerConfig){
204
+				container.WithName(ctrName),
205
+				container.WithCmd("top"),
206
+				container.WithImage("busybox:latest"),
207
+			}
208
+			// Don't specify the network name for the bridge network, because that
209
+			// exercises a different code path (the network name isn't set until the
210
+			// container starts, until then it's "default").
211
+			if tc.netName != "bridge" {
212
+				opts = append(opts, container.WithNetworkMode(tc.netName))
213
+			}
214
+			if tc.desiredMAC != "" {
215
+				if tc.ctrWide {
216
+					opts = append(opts, container.WithContainerWideMacAddress(tc.desiredMAC))
217
+				} else {
218
+					opts = append(opts, container.WithMacAddress(tc.netName, tc.desiredMAC))
219
+				}
220
+			}
221
+			id := container.Create(ctx, t, c, opts...)
222
+			defer c.ContainerRemove(ctx, id, containertypes.RemoveOptions{
223
+				Force: true,
224
+			})
225
+
226
+			inspect := container.Inspect(ctx, t, c, ctrName)
227
+			configMAC := inspect.Config.MacAddress //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.44.
228
+			assert.Check(t, is.DeepEqual(configMAC, tc.desiredMAC))
229
+		})
230
+	}
231
+}