From 6cc6682f5f3c75a58574623ff9e66e7ed5cc3ed4 Mon Sep 17 00:00:00 2001 From: Albin Kerouanton Date: Wed, 6 Sep 2023 17:50:56 +0200 Subject: [PATCH] daemon: fix under what conditions container's mac-address is applied The daemon would pass an EndpointCreateOption to set the interface MAC address if the network name and the provided network mode were matching. Obviously, if the network mode is a network ID, it won't work. To make things worse, the network mode is never normalized if it's a partial ID. To fix that: 1. the condition under what the container's mac-address is applied is updated to also match the full ID; 2. the network mode is normalized to a full ID when it's only a partial one. Signed-off-by: Albin Kerouanton --- daemon/container_operations.go | 25 ++++++++++++++++++++ daemon/network.go | 3 ++- integration/container/run_linux_test.go | 26 +++++++++++++++++++++ integration/internal/container/container.go | 16 +++++++++++++ integration/internal/container/ops.go | 12 ++++++++++ 5 files changed, 81 insertions(+), 1 deletion(-) diff --git a/daemon/container_operations.go b/daemon/container_operations.go index 5bb2ba5e1b..a87cccb355 100644 --- a/daemon/container_operations.go +++ b/daemon/container_operations.go @@ -691,6 +691,12 @@ func (daemon *Daemon) connectToNetwork(cfg *config.Config, container *container. } nwName := n.Name() + if idOrName != container.HostConfig.NetworkMode.NetworkName() { + if err := daemon.normalizeNetMode(container); err != nil { + return err + } + } + var operIPAM bool if nwCfg != nil { if epConfig, ok := nwCfg.EndpointsConfig[nwName]; ok { @@ -886,6 +892,25 @@ func (daemon *Daemon) tryDetachContainerFromClusterNetwork(network *libnetwork.N }) } +// normalizeNetMode checks whether the network mode references a network by a partial ID. In that case, it replaces the +// partial ID with the full network ID. +// TODO(aker): transform ID into name when the referenced network is one of the predefined. +func (daemon *Daemon) normalizeNetMode(container *container.Container) error { + if container.HostConfig.NetworkMode.IsUserDefined() { + netMode := container.HostConfig.NetworkMode.NetworkName() + nw, err := daemon.FindNetwork(netMode) + if err != nil { + return fmt.Errorf("could not find a network matching network mode %s: %w", netMode, err) + } + + if netMode != nw.ID() && netMode != nw.Name() { + container.HostConfig.NetworkMode = containertypes.NetworkMode(nw.ID()) + } + } + + return nil +} + func (daemon *Daemon) initializeNetworking(cfg *config.Config, container *container.Container) error { if container.HostConfig.NetworkMode.IsContainer() { // we need to get the hosts files from the container to join diff --git a/daemon/network.go b/daemon/network.go index b7b6454172..47dc5b6e8c 100644 --- a/daemon/network.go +++ b/daemon/network.go @@ -870,7 +870,8 @@ func buildCreateEndpointOptions(c *container.Container, n *libnetwork.Network, e // to which container was connected to on docker run. // Ideally all these network-specific endpoint configurations must be moved under // container.NetworkSettings.Networks[n.Name()] - if nwName == c.HostConfig.NetworkMode.NetworkName() || (nwName == defaultNetName && c.HostConfig.NetworkMode.IsDefault()) { + netMode := c.HostConfig.NetworkMode + if nwName == netMode.NetworkName() || n.ID() == netMode.NetworkName() || (nwName == defaultNetName && netMode.IsDefault()) { if c.Config.MacAddress != "" { mac, err := net.ParseMAC(c.Config.MacAddress) if err != nil { diff --git a/integration/container/run_linux_test.go b/integration/container/run_linux_test.go index cd34077aa2..1c55599748 100644 --- a/integration/container/run_linux_test.go +++ b/integration/container/run_linux_test.go @@ -286,3 +286,29 @@ func TestRunWithAlternativeContainerdShim(t *testing.T) { assert.Equal(t, strings.TrimSpace(b.String()), "Hello, world!") } + +func TestMacAddressIsAppliedToMainNetworkWithShortID(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 := d.NewClientT(t) + + n := net.CreateNoError(ctx, t, apiClient, "testnet", net.WithIPAM("192.168.101.0/24", "192.168.101.1")) + + cid := container.Run(ctx, t, apiClient, + container.WithImage("busybox:latest"), + container.WithCmd("/bin/sleep", "infinity"), + container.WithStopSignal("SIGKILL"), + container.WithNetworkMode(n[:10]), + container.WithMacAddress("02:42:08:26:a9:55")) + defer container.Remove(ctx, t, apiClient, cid, types.ContainerRemoveOptions{Force: true}) + + c := container.Inspect(ctx, t, apiClient, cid) + assert.Equal(t, c.NetworkSettings.Networks["testnet"].MacAddress, "02:42:08:26:a9:55") +} diff --git a/integration/internal/container/container.go b/integration/internal/container/container.go index 50ed6a3c7a..2eb04ef0ae 100644 --- a/integration/internal/container/container.go +++ b/integration/internal/container/container.go @@ -154,3 +154,19 @@ func demultiplexStreams(ctx context.Context, resp types.HijackedResponse) (strea wg.Wait() return s, err } + +func Remove(ctx context.Context, t *testing.T, apiClient client.APIClient, container string, options types.ContainerRemoveOptions) { + t.Helper() + + err := apiClient.ContainerRemove(ctx, container, options) + assert.NilError(t, err) +} + +func Inspect(ctx context.Context, t *testing.T, apiClient client.APIClient, containerRef string) types.ContainerJSON { + t.Helper() + + c, err := apiClient.ContainerInspect(ctx, containerRef) + assert.NilError(t, err) + + return c +} diff --git a/integration/internal/container/ops.go b/integration/internal/container/ops.go index 72146d0a12..8b9ad9d22e 100644 --- a/integration/internal/container/ops.go +++ b/integration/internal/container/ops.go @@ -280,3 +280,15 @@ func WithPIDMode(mode container.PidMode) func(c *TestContainerConfig) { c.HostConfig.PidMode = mode } } + +func WithStopSignal(stopSignal string) func(c *TestContainerConfig) { + return func(c *TestContainerConfig) { + c.Config.StopSignal = stopSignal + } +} + +func WithMacAddress(address string) func(c *TestContainerConfig) { + return func(c *TestContainerConfig) { + c.Config.MacAddress = address + } +}