diff --git a/daemon/create.go b/daemon/create.go index 83e1174d7d912..166af3bf73d28 100644 --- a/daemon/create.go +++ b/daemon/create.go @@ -26,6 +26,11 @@ func (daemon *Daemon) ContainerCreate(params types.ContainerCreateConfig) (types return types.ContainerCreateResponse{Warnings: warnings}, err } + err = daemon.verifyNetworkingConfig(params.NetworkingConfig) + if err != nil { + return types.ContainerCreateResponse{}, err + } + if params.HostConfig == nil { params.HostConfig = &containertypes.HostConfig{} } diff --git a/daemon/daemon.go b/daemon/daemon.go index 671383149f89c..8af07f38c6bac 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -31,6 +31,7 @@ import ( containertypes "github.com/docker/engine-api/types/container" eventtypes "github.com/docker/engine-api/types/events" "github.com/docker/engine-api/types/filters" + networktypes "github.com/docker/engine-api/types/network" registrytypes "github.com/docker/engine-api/types/registry" "github.com/docker/engine-api/types/strslice" // register graph drivers @@ -1419,6 +1420,18 @@ func (daemon *Daemon) verifyContainerSettings(hostConfig *containertypes.HostCon return verifyPlatformContainerSettings(daemon, hostConfig, config) } +// Checks if the client set configurations for more than one network while creating a container +func (daemon *Daemon) verifyNetworkingConfig(nwConfig *networktypes.NetworkingConfig) error { + if nwConfig == nil || len(nwConfig.EndpointsConfig) <= 1 { + return nil + } + l := make([]string, 0, len(nwConfig.EndpointsConfig)) + for k := range nwConfig.EndpointsConfig { + l = append(l, k) + } + return derr.ErrorCodeMultipleNetworkConnect.WithArgs(fmt.Sprintf("%v", l)) +} + func configureVolumes(config *Config, rootUID, rootGID int) (*store.VolumeStore, error) { volumesDriver, err := local.New(config.Root, rootUID, rootGID) if err != nil { diff --git a/errors/daemon.go b/errors/daemon.go index 644dcdbe68e3c..a2caabb16b113 100644 --- a/errors/daemon.go +++ b/errors/daemon.go @@ -966,4 +966,13 @@ var ( Description: "Engine's predefined networks cannot be deleted", HTTPStatusCode: http.StatusForbidden, }) + + // ErrorCodeMultipleNetworkConnect is generated when more than one network is passed + // when creating a container + ErrorCodeMultipleNetworkConnect = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "CANNOT_CONNECT_TO_MULTIPLE_NETWORKS", + Message: "Container cannot be connected to %s", + Description: "A container can only be connected to one network at the time", + HTTPStatusCode: http.StatusBadRequest, + }) ) diff --git a/integration-cli/docker_api_containers_test.go b/integration-cli/docker_api_containers_test.go index ca5de07540b97..bdded2c5b8892 100644 --- a/integration-cli/docker_api_containers_test.go +++ b/integration-cli/docker_api_containers_test.go @@ -20,6 +20,7 @@ import ( "github.com/docker/docker/pkg/stringid" "github.com/docker/engine-api/types" containertypes "github.com/docker/engine-api/types/container" + networktypes "github.com/docker/engine-api/types/network" "github.com/go-check/check" ) @@ -604,6 +605,29 @@ func (s *DockerSuite) TestContainerApiCreateEmptyConfig(c *check.C) { c.Assert(string(b), checker.Equals, expected) } +func (s *DockerSuite) TestContainerApiCreateMultipleNetworksConfig(c *check.C) { + // Container creation must fail if client specified configurations for more than one network + config := map[string]interface{}{ + "Image": "busybox", + "NetworkingConfig": networktypes.NetworkingConfig{ + EndpointsConfig: map[string]*networktypes.EndpointSettings{ + "net1": {}, + "net2": {}, + "net3": {}, + }, + }, + } + + status, b, err := sockRequest("POST", "/containers/create", config) + c.Assert(err, checker.IsNil) + c.Assert(status, checker.Equals, http.StatusBadRequest) + // network name order in error message is not deterministic + c.Assert(string(b), checker.Contains, "Container cannot be connected to [") + c.Assert(string(b), checker.Contains, "net1") + c.Assert(string(b), checker.Contains, "net2") + c.Assert(string(b), checker.Contains, "net3") +} + func (s *DockerSuite) TestContainerApiCreateWithHostName(c *check.C) { testRequires(c, DaemonIsLinux) hostName := "test-host"