Support `--link` for user-defined networks

This brings in the container-local alias functionality for containers
connected to u ser-defined networks.

Signed-off-by: Madhu Venugopal <madhu@docker.com>
This commit is contained in:
Madhu Venugopal 2016-01-05 11:20:47 -08:00
parent c1582f20cc
commit e221b8a3d6
8 changed files with 157 additions and 11 deletions

View File

@ -10,6 +10,7 @@ import (
"github.com/docker/docker/opts"
flag "github.com/docker/docker/pkg/mflag"
"github.com/docker/docker/pkg/stringid"
runconfigopts "github.com/docker/docker/runconfig/opts"
"github.com/docker/engine-api/types"
"github.com/docker/engine-api/types/filters"
"github.com/docker/engine-api/types/network"
@ -112,6 +113,8 @@ func (cli *DockerCli) CmdNetworkConnect(args ...string) error {
cmd := Cli.Subcmd("network connect", []string{"NETWORK CONTAINER"}, "Connects a container to a network", false)
flIPAddress := cmd.String([]string{"-ip"}, "", "IP Address")
flIPv6Address := cmd.String([]string{"-ip6"}, "", "IPv6 Address")
flLinks := opts.NewListOpts(runconfigopts.ValidateLink)
cmd.Var(&flLinks, []string{"-link"}, "Add link to another container")
cmd.Require(flag.Min, 2)
if err := cmd.ParseFlags(args, true); err != nil {
return err
@ -121,6 +124,7 @@ func (cli *DockerCli) CmdNetworkConnect(args ...string) error {
IPv4Address: *flIPAddress,
IPv6Address: *flIPv6Address,
},
Links: flLinks.GetAll(),
}
return cli.client.NetworkConnect(cmd.Arg(0), cmd.Arg(1), epConfig)
}

View File

@ -18,6 +18,7 @@ import (
"github.com/docker/docker/pkg/chrootarchive"
"github.com/docker/docker/pkg/symlink"
"github.com/docker/docker/pkg/system"
runconfigopts "github.com/docker/docker/runconfig/opts"
"github.com/docker/docker/utils"
"github.com/docker/docker/volume"
"github.com/docker/engine-api/types/container"
@ -247,6 +248,21 @@ func (container *Container) UpdateSandboxNetworkSettings(sb libnetwork.Sandbox)
return nil
}
// BuildJoinOptions builds endpoint Join options from a given network.
func (container *Container) BuildJoinOptions(n libnetwork.Network) ([]libnetwork.EndpointOption, error) {
var joinOptions []libnetwork.EndpointOption
if epConfig, ok := container.NetworkSettings.Networks[n.Name()]; ok {
for _, str := range epConfig.Links {
name, alias, err := runconfigopts.ParseLink(str)
if err != nil {
return nil, err
}
joinOptions = append(joinOptions, libnetwork.CreateOptionAlias(name, alias))
}
}
return joinOptions, nil
}
// BuildCreateEndpointOptions builds endpoint options from a given network.
func (container *Container) BuildCreateEndpointOptions(n libnetwork.Network) ([]libnetwork.EndpointOption, error) {
var (

View File

@ -586,7 +586,7 @@ func (daemon *Daemon) updateContainerNetworkSettings(container *container.Contai
if container.NetworkSettings == nil {
container.NetworkSettings = &network.Settings{}
}
if endpointsConfig != nil {
if len(endpointsConfig) > 0 {
container.NetworkSettings.Networks = endpointsConfig
}
if container.NetworkSettings.Networks == nil {
@ -816,7 +816,12 @@ func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName
container.UpdateSandboxNetworkSettings(sb)
}
if err := ep.Join(sb); err != nil {
joinOptions, err := container.BuildJoinOptions(n)
if err != nil {
return err
}
if err := ep.Join(sb, joinOptions...); err != nil {
return err
}

View File

@ -868,7 +868,7 @@ func setupDaemonRoot(config *Config, rootDir string, rootUID, rootGID int) error
// registerLinks writes the links to a file.
func (daemon *Daemon) registerLinks(container *container.Container, hostConfig *containertypes.HostConfig) error {
if hostConfig == nil {
if hostConfig == nil || hostConfig.NetworkMode.IsUserDefined() {
return nil
}

View File

@ -1040,3 +1040,44 @@ func verifyIPAddresses(c *check.C, cName, nwname, ipv4, ipv6 string) {
out, _ = dockerCmd(c, "inspect", fmt.Sprintf("--format='{{ .NetworkSettings.Networks.%s.GlobalIPv6Address }}'", nwname), cName)
c.Assert(strings.TrimSpace(out), check.Equals, ipv6)
}
func (s *DockerSuite) TestUserDefinedNetworkConnectDisconnectLink(c *check.C) {
testRequires(c, DaemonIsLinux, NotUserNamespace)
dockerCmd(c, "network", "create", "-d", "bridge", "foo1")
dockerCmd(c, "network", "create", "-d", "bridge", "foo2")
dockerCmd(c, "run", "-d", "--net=foo1", "--name=first", "busybox", "top")
c.Assert(waitRun("first"), check.IsNil)
// run a container in user-defined network udlinkNet with a link for an existing container
// and a link for a container that doesnt exist
dockerCmd(c, "run", "-d", "--net=foo1", "--name=second", "--link=first:FirstInFoo1",
"--link=third:bar", "busybox", "top")
c.Assert(waitRun("second"), check.IsNil)
// ping to first and its alias FirstInFoo1 must succeed
_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
c.Assert(err, check.IsNil)
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "FirstInFoo1")
c.Assert(err, check.IsNil)
// connect first container to foo2 network
dockerCmd(c, "network", "connect", "foo2", "first")
// connect second container to foo2 network with a different alias for first container
dockerCmd(c, "network", "connect", "--link=first:FirstInFoo2", "foo2", "second")
// ping the new alias in network foo2
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "FirstInFoo2")
c.Assert(err, check.IsNil)
// disconnect first container from foo1 network
dockerCmd(c, "network", "disconnect", "foo1", "first")
// link in foo1 network must fail
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "FirstInFoo1")
c.Assert(err, check.NotNil)
// link in foo2 network must succeed
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "FirstInFoo2")
c.Assert(err, check.IsNil)
}

View File

@ -199,6 +199,80 @@ func (s *DockerSuite) TestRunLinksContainerWithContainerId(c *check.C) {
}
}
func (s *DockerSuite) TestUserDefinedNetworkLinks(c *check.C) {
testRequires(c, DaemonIsLinux, NotUserNamespace)
dockerCmd(c, "network", "create", "-d", "bridge", "udlinkNet")
dockerCmd(c, "run", "-d", "--net=udlinkNet", "--name=first", "busybox", "top")
c.Assert(waitRun("first"), check.IsNil)
// run a container in user-defined network udlinkNet with a link for an existing container
// and a link for a container that doesnt exist
dockerCmd(c, "run", "-d", "--net=udlinkNet", "--name=second", "--link=first:foo",
"--link=third:bar", "busybox", "top")
c.Assert(waitRun("second"), check.IsNil)
// ping to first and its alias foo must succeed
_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
c.Assert(err, check.IsNil)
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo")
c.Assert(err, check.IsNil)
// ping to third and its alias must fail
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "third")
c.Assert(err, check.NotNil)
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "bar")
c.Assert(err, check.NotNil)
// start third container now
dockerCmd(c, "run", "-d", "--net=udlinkNet", "--name=third", "busybox", "top")
c.Assert(waitRun("third"), check.IsNil)
// ping to third and its alias must succeed now
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "third")
c.Assert(err, check.IsNil)
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "bar")
c.Assert(err, check.IsNil)
}
func (s *DockerSuite) TestUserDefinedNetworkLinksWithRestart(c *check.C) {
testRequires(c, DaemonIsLinux, NotUserNamespace)
dockerCmd(c, "network", "create", "-d", "bridge", "udlinkNet")
dockerCmd(c, "run", "-d", "--net=udlinkNet", "--name=first", "busybox", "top")
c.Assert(waitRun("first"), check.IsNil)
dockerCmd(c, "run", "-d", "--net=udlinkNet", "--name=second", "--link=first:foo",
"busybox", "top")
c.Assert(waitRun("second"), check.IsNil)
// ping to first and its alias foo must succeed
_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
c.Assert(err, check.IsNil)
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo")
c.Assert(err, check.IsNil)
// Restart first container
dockerCmd(c, "restart", "first")
c.Assert(waitRun("first"), check.IsNil)
// ping to first and its alias foo must still succeed
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
c.Assert(err, check.IsNil)
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo")
c.Assert(err, check.IsNil)
// Restart second container
dockerCmd(c, "restart", "second")
c.Assert(waitRun("second"), check.IsNil)
// ping to first and its alias foo must still succeed
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
c.Assert(err, check.IsNil)
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo")
c.Assert(err, check.IsNil)
}
// Issue 9677.
func (s *DockerSuite) TestRunWithDaemonFlags(c *check.C) {
out, _, err := dockerCmdWithError("--exec-opt", "foo=bar", "run", "-i", "busybox", "true")

View File

@ -48,10 +48,6 @@ func ValidateNetMode(c *container.Config, hc *container.HostConfig) error {
return ErrConflictContainerNetworkAndLinks
}
if hc.NetworkMode.IsUserDefined() && len(hc.Links) > 0 {
return ErrConflictUserDefinedNetworkAndLinks
}
if (hc.NetworkMode.IsHost() || hc.NetworkMode.IsContainer()) && len(hc.DNS) > 0 {
return ErrConflictNetworkAndDNS
}

View File

@ -409,11 +409,11 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
config.StdinOnce = true
}
var networkingConfig *networktypes.NetworkingConfig
networkingConfig := &networktypes.NetworkingConfig{
EndpointsConfig: make(map[string]*networktypes.EndpointSettings),
}
if *flIPv4Address != "" || *flIPv6Address != "" {
networkingConfig = &networktypes.NetworkingConfig{
EndpointsConfig: make(map[string]*networktypes.EndpointSettings),
}
networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = &networktypes.EndpointSettings{
IPAMConfig: &networktypes.EndpointIPAMConfig{
IPv4Address: *flIPv4Address,
@ -422,6 +422,16 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
}
}
if hostConfig.NetworkMode.IsUserDefined() && len(hostConfig.Links) > 0 {
epConfig := networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)]
if epConfig == nil {
epConfig = &networktypes.EndpointSettings{}
}
epConfig.Links = make([]string, len(hostConfig.Links))
copy(epConfig.Links, hostConfig.Links)
networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = epConfig
}
return config, hostConfig, networkingConfig, cmd, nil
}