diff --git a/api/server/router/network/network_routes.go b/api/server/router/network/network_routes.go index ffd0f392d0..450d73b4fe 100644 --- a/api/server/router/network/network_routes.go +++ b/api/server/router/network/network_routes.go @@ -275,12 +275,11 @@ func (n *networkRouter) postNetworkConnect(ctx context.Context, w http.ResponseW return err } - // Always make sure there is no ambiguity with respect to the network ID/name - nw, err := n.backend.FindNetwork(vars["id"]) - if err != nil { - return err - } - return n.backend.ConnectContainerToNetwork(connect.Container, nw.ID(), connect.EndpointConfig) + // Unlike other operations, we does not check ambiguity of the name/ID here. + // The reason is that, In case of attachable network in swarm scope, the actual local network + // may not be available at the time. At the same time, inside daemon `ConnectContainerToNetwork` + // does the ambiguity check anyway. Therefore, passing the name to daemon would be enough. + return n.backend.ConnectContainerToNetwork(connect.Container, vars["id"], connect.EndpointConfig) } func (n *networkRouter) postNetworkDisconnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { diff --git a/daemon/container_operations.go b/daemon/container_operations.go index 3900e95e8d..a1a64fec3e 100644 --- a/daemon/container_operations.go +++ b/daemon/container_operations.go @@ -20,6 +20,7 @@ import ( "github.com/docker/docker/runconfig" "github.com/docker/go-connections/nat" "github.com/docker/libnetwork" + netconst "github.com/docker/libnetwork/datastore" "github.com/docker/libnetwork/netlabel" "github.com/docker/libnetwork/options" "github.com/docker/libnetwork/types" @@ -259,6 +260,13 @@ func (daemon *Daemon) updateNetworkSettings(container *container.Container, n li } if sn.Name() == n.Name() { + // If the network scope is swarm, then this + // is an attachable network, which may not + // be locally available previously. + // So always update. + if n.Info().Scope() == netconst.SwarmScope { + continue + } // Avoid duplicate config return nil } @@ -272,10 +280,8 @@ func (daemon *Daemon) updateNetworkSettings(container *container.Container, n li } } - if _, ok := container.NetworkSettings.Networks[n.Name()]; !ok { - container.NetworkSettings.Networks[n.Name()] = &network.EndpointSettings{ - EndpointSettings: endpointConfig, - } + container.NetworkSettings.Networks[n.Name()] = &network.EndpointSettings{ + EndpointSettings: endpointConfig, } return nil diff --git a/integration/service/network_test.go b/integration/service/network_test.go new file mode 100644 index 0000000000..016bd2ce4b --- /dev/null +++ b/integration/service/network_test.go @@ -0,0 +1,86 @@ +package service + +import ( + "context" + "testing" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/network" + "github.com/docker/docker/integration-cli/request" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestDockerNetworkConnectAlias(t *testing.T) { + defer setupTest(t)() + d := newSwarm(t) + defer d.Stop(t) + client, err := request.NewClientForHost(d.Sock()) + require.NoError(t, err) + ctx := context.Background() + + name := "test-alias" + _, err = client.NetworkCreate(ctx, name, types.NetworkCreate{ + Driver: "overlay", + Attachable: true, + }) + require.NoError(t, err) + _, err = client.ContainerCreate(ctx, + &container.Config{ + Cmd: []string{"top"}, + Image: "busybox", + }, + &container.HostConfig{}, + &network.NetworkingConfig{ + EndpointsConfig: map[string]*network.EndpointSettings{ + name: {}, + }, + }, + "ng1", + ) + require.NoError(t, err) + err = client.NetworkConnect(ctx, name, "ng1", &network.EndpointSettings{ + Aliases: []string{ + "aaa", + }, + }) + require.NoError(t, err) + + err = client.ContainerStart(ctx, "ng1", types.ContainerStartOptions{}) + require.NoError(t, err) + + ng1, err := client.ContainerInspect(ctx, "ng1") + require.NoError(t, err) + assert.Equal(t, len(ng1.NetworkSettings.Networks[name].Aliases), 2) + assert.Equal(t, ng1.NetworkSettings.Networks[name].Aliases[0], "aaa") + + _, err = client.ContainerCreate(ctx, + &container.Config{ + Cmd: []string{"top"}, + Image: "busybox", + }, + &container.HostConfig{}, + &network.NetworkingConfig{ + EndpointsConfig: map[string]*network.EndpointSettings{ + name: {}, + }, + }, + "ng2", + ) + require.NoError(t, err) + err = client.NetworkConnect(ctx, name, "ng2", &network.EndpointSettings{ + Aliases: []string{ + "bbb", + }, + }) + require.NoError(t, err) + + err = client.ContainerStart(ctx, "ng2", types.ContainerStartOptions{}) + require.NoError(t, err) + + ng2, err := client.ContainerInspect(ctx, "ng2") + require.NoError(t, err) + assert.Equal(t, len(ng2.NetworkSettings.Networks[name].Aliases), 2) + assert.Equal(t, ng2.NetworkSettings.Networks[name].Aliases[0], "bbb") +}