This brings in the container-local alias functionality for containers
connected to u ser-defined networks.
Signed-off-by: Madhu Venugopal <madhu@docker.com>
| ... | ... |
@@ -10,6 +10,7 @@ import ( |
| 10 | 10 |
"github.com/docker/docker/opts" |
| 11 | 11 |
flag "github.com/docker/docker/pkg/mflag" |
| 12 | 12 |
"github.com/docker/docker/pkg/stringid" |
| 13 |
+ runconfigopts "github.com/docker/docker/runconfig/opts" |
|
| 13 | 14 |
"github.com/docker/engine-api/types" |
| 14 | 15 |
"github.com/docker/engine-api/types/filters" |
| 15 | 16 |
"github.com/docker/engine-api/types/network" |
| ... | ... |
@@ -112,6 +113,8 @@ func (cli *DockerCli) CmdNetworkConnect(args ...string) error {
|
| 112 | 112 |
cmd := Cli.Subcmd("network connect", []string{"NETWORK CONTAINER"}, "Connects a container to a network", false)
|
| 113 | 113 |
flIPAddress := cmd.String([]string{"-ip"}, "", "IP Address")
|
| 114 | 114 |
flIPv6Address := cmd.String([]string{"-ip6"}, "", "IPv6 Address")
|
| 115 |
+ flLinks := opts.NewListOpts(runconfigopts.ValidateLink) |
|
| 116 |
+ cmd.Var(&flLinks, []string{"-link"}, "Add link to another container")
|
|
| 115 | 117 |
cmd.Require(flag.Min, 2) |
| 116 | 118 |
if err := cmd.ParseFlags(args, true); err != nil {
|
| 117 | 119 |
return err |
| ... | ... |
@@ -121,6 +124,7 @@ func (cli *DockerCli) CmdNetworkConnect(args ...string) error {
|
| 121 | 121 |
IPv4Address: *flIPAddress, |
| 122 | 122 |
IPv6Address: *flIPv6Address, |
| 123 | 123 |
}, |
| 124 |
+ Links: flLinks.GetAll(), |
|
| 124 | 125 |
} |
| 125 | 126 |
return cli.client.NetworkConnect(cmd.Arg(0), cmd.Arg(1), epConfig) |
| 126 | 127 |
} |
| ... | ... |
@@ -18,6 +18,7 @@ import ( |
| 18 | 18 |
"github.com/docker/docker/pkg/chrootarchive" |
| 19 | 19 |
"github.com/docker/docker/pkg/symlink" |
| 20 | 20 |
"github.com/docker/docker/pkg/system" |
| 21 |
+ runconfigopts "github.com/docker/docker/runconfig/opts" |
|
| 21 | 22 |
"github.com/docker/docker/utils" |
| 22 | 23 |
"github.com/docker/docker/volume" |
| 23 | 24 |
"github.com/docker/engine-api/types/container" |
| ... | ... |
@@ -247,6 +248,21 @@ func (container *Container) UpdateSandboxNetworkSettings(sb libnetwork.Sandbox) |
| 247 | 247 |
return nil |
| 248 | 248 |
} |
| 249 | 249 |
|
| 250 |
+// BuildJoinOptions builds endpoint Join options from a given network. |
|
| 251 |
+func (container *Container) BuildJoinOptions(n libnetwork.Network) ([]libnetwork.EndpointOption, error) {
|
|
| 252 |
+ var joinOptions []libnetwork.EndpointOption |
|
| 253 |
+ if epConfig, ok := container.NetworkSettings.Networks[n.Name()]; ok {
|
|
| 254 |
+ for _, str := range epConfig.Links {
|
|
| 255 |
+ name, alias, err := runconfigopts.ParseLink(str) |
|
| 256 |
+ if err != nil {
|
|
| 257 |
+ return nil, err |
|
| 258 |
+ } |
|
| 259 |
+ joinOptions = append(joinOptions, libnetwork.CreateOptionAlias(name, alias)) |
|
| 260 |
+ } |
|
| 261 |
+ } |
|
| 262 |
+ return joinOptions, nil |
|
| 263 |
+} |
|
| 264 |
+ |
|
| 250 | 265 |
// BuildCreateEndpointOptions builds endpoint options from a given network. |
| 251 | 266 |
func (container *Container) BuildCreateEndpointOptions(n libnetwork.Network) ([]libnetwork.EndpointOption, error) {
|
| 252 | 267 |
var ( |
| ... | ... |
@@ -586,7 +586,7 @@ func (daemon *Daemon) updateContainerNetworkSettings(container *container.Contai |
| 586 | 586 |
if container.NetworkSettings == nil {
|
| 587 | 587 |
container.NetworkSettings = &network.Settings{}
|
| 588 | 588 |
} |
| 589 |
- if endpointsConfig != nil {
|
|
| 589 |
+ if len(endpointsConfig) > 0 {
|
|
| 590 | 590 |
container.NetworkSettings.Networks = endpointsConfig |
| 591 | 591 |
} |
| 592 | 592 |
if container.NetworkSettings.Networks == nil {
|
| ... | ... |
@@ -816,7 +816,12 @@ func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName |
| 816 | 816 |
container.UpdateSandboxNetworkSettings(sb) |
| 817 | 817 |
} |
| 818 | 818 |
|
| 819 |
- if err := ep.Join(sb); err != nil {
|
|
| 819 |
+ joinOptions, err := container.BuildJoinOptions(n) |
|
| 820 |
+ if err != nil {
|
|
| 821 |
+ return err |
|
| 822 |
+ } |
|
| 823 |
+ |
|
| 824 |
+ if err := ep.Join(sb, joinOptions...); err != nil {
|
|
| 820 | 825 |
return err |
| 821 | 826 |
} |
| 822 | 827 |
|
| ... | ... |
@@ -868,7 +868,7 @@ func setupDaemonRoot(config *Config, rootDir string, rootUID, rootGID int) error |
| 868 | 868 |
|
| 869 | 869 |
// registerLinks writes the links to a file. |
| 870 | 870 |
func (daemon *Daemon) registerLinks(container *container.Container, hostConfig *containertypes.HostConfig) error {
|
| 871 |
- if hostConfig == nil {
|
|
| 871 |
+ if hostConfig == nil || hostConfig.NetworkMode.IsUserDefined() {
|
|
| 872 | 872 |
return nil |
| 873 | 873 |
} |
| 874 | 874 |
|
| ... | ... |
@@ -1040,3 +1040,44 @@ func verifyIPAddresses(c *check.C, cName, nwname, ipv4, ipv6 string) {
|
| 1040 | 1040 |
out, _ = dockerCmd(c, "inspect", fmt.Sprintf("--format='{{ .NetworkSettings.Networks.%s.GlobalIPv6Address }}'", nwname), cName)
|
| 1041 | 1041 |
c.Assert(strings.TrimSpace(out), check.Equals, ipv6) |
| 1042 | 1042 |
} |
| 1043 |
+ |
|
| 1044 |
+func (s *DockerSuite) TestUserDefinedNetworkConnectDisconnectLink(c *check.C) {
|
|
| 1045 |
+ testRequires(c, DaemonIsLinux, NotUserNamespace) |
|
| 1046 |
+ dockerCmd(c, "network", "create", "-d", "bridge", "foo1") |
|
| 1047 |
+ dockerCmd(c, "network", "create", "-d", "bridge", "foo2") |
|
| 1048 |
+ |
|
| 1049 |
+ dockerCmd(c, "run", "-d", "--net=foo1", "--name=first", "busybox", "top") |
|
| 1050 |
+ c.Assert(waitRun("first"), check.IsNil)
|
|
| 1051 |
+ |
|
| 1052 |
+ // run a container in user-defined network udlinkNet with a link for an existing container |
|
| 1053 |
+ // and a link for a container that doesnt exist |
|
| 1054 |
+ dockerCmd(c, "run", "-d", "--net=foo1", "--name=second", "--link=first:FirstInFoo1", |
|
| 1055 |
+ "--link=third:bar", "busybox", "top") |
|
| 1056 |
+ c.Assert(waitRun("second"), check.IsNil)
|
|
| 1057 |
+ |
|
| 1058 |
+ // ping to first and its alias FirstInFoo1 must succeed |
|
| 1059 |
+ _, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
|
|
| 1060 |
+ c.Assert(err, check.IsNil) |
|
| 1061 |
+ _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "FirstInFoo1")
|
|
| 1062 |
+ c.Assert(err, check.IsNil) |
|
| 1063 |
+ |
|
| 1064 |
+ // connect first container to foo2 network |
|
| 1065 |
+ dockerCmd(c, "network", "connect", "foo2", "first") |
|
| 1066 |
+ // connect second container to foo2 network with a different alias for first container |
|
| 1067 |
+ dockerCmd(c, "network", "connect", "--link=first:FirstInFoo2", "foo2", "second") |
|
| 1068 |
+ |
|
| 1069 |
+ // ping the new alias in network foo2 |
|
| 1070 |
+ _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "FirstInFoo2")
|
|
| 1071 |
+ c.Assert(err, check.IsNil) |
|
| 1072 |
+ |
|
| 1073 |
+ // disconnect first container from foo1 network |
|
| 1074 |
+ dockerCmd(c, "network", "disconnect", "foo1", "first") |
|
| 1075 |
+ |
|
| 1076 |
+ // link in foo1 network must fail |
|
| 1077 |
+ _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "FirstInFoo1")
|
|
| 1078 |
+ c.Assert(err, check.NotNil) |
|
| 1079 |
+ |
|
| 1080 |
+ // link in foo2 network must succeed |
|
| 1081 |
+ _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "FirstInFoo2")
|
|
| 1082 |
+ c.Assert(err, check.IsNil) |
|
| 1083 |
+} |
| ... | ... |
@@ -199,6 +199,80 @@ func (s *DockerSuite) TestRunLinksContainerWithContainerId(c *check.C) {
|
| 199 | 199 |
} |
| 200 | 200 |
} |
| 201 | 201 |
|
| 202 |
+func (s *DockerSuite) TestUserDefinedNetworkLinks(c *check.C) {
|
|
| 203 |
+ testRequires(c, DaemonIsLinux, NotUserNamespace) |
|
| 204 |
+ dockerCmd(c, "network", "create", "-d", "bridge", "udlinkNet") |
|
| 205 |
+ |
|
| 206 |
+ dockerCmd(c, "run", "-d", "--net=udlinkNet", "--name=first", "busybox", "top") |
|
| 207 |
+ c.Assert(waitRun("first"), check.IsNil)
|
|
| 208 |
+ |
|
| 209 |
+ // run a container in user-defined network udlinkNet with a link for an existing container |
|
| 210 |
+ // and a link for a container that doesnt exist |
|
| 211 |
+ dockerCmd(c, "run", "-d", "--net=udlinkNet", "--name=second", "--link=first:foo", |
|
| 212 |
+ "--link=third:bar", "busybox", "top") |
|
| 213 |
+ c.Assert(waitRun("second"), check.IsNil)
|
|
| 214 |
+ |
|
| 215 |
+ // ping to first and its alias foo must succeed |
|
| 216 |
+ _, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
|
|
| 217 |
+ c.Assert(err, check.IsNil) |
|
| 218 |
+ _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo")
|
|
| 219 |
+ c.Assert(err, check.IsNil) |
|
| 220 |
+ |
|
| 221 |
+ // ping to third and its alias must fail |
|
| 222 |
+ _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "third")
|
|
| 223 |
+ c.Assert(err, check.NotNil) |
|
| 224 |
+ _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "bar")
|
|
| 225 |
+ c.Assert(err, check.NotNil) |
|
| 226 |
+ |
|
| 227 |
+ // start third container now |
|
| 228 |
+ dockerCmd(c, "run", "-d", "--net=udlinkNet", "--name=third", "busybox", "top") |
|
| 229 |
+ c.Assert(waitRun("third"), check.IsNil)
|
|
| 230 |
+ |
|
| 231 |
+ // ping to third and its alias must succeed now |
|
| 232 |
+ _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "third")
|
|
| 233 |
+ c.Assert(err, check.IsNil) |
|
| 234 |
+ _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "bar")
|
|
| 235 |
+ c.Assert(err, check.IsNil) |
|
| 236 |
+} |
|
| 237 |
+ |
|
| 238 |
+func (s *DockerSuite) TestUserDefinedNetworkLinksWithRestart(c *check.C) {
|
|
| 239 |
+ testRequires(c, DaemonIsLinux, NotUserNamespace) |
|
| 240 |
+ dockerCmd(c, "network", "create", "-d", "bridge", "udlinkNet") |
|
| 241 |
+ |
|
| 242 |
+ dockerCmd(c, "run", "-d", "--net=udlinkNet", "--name=first", "busybox", "top") |
|
| 243 |
+ c.Assert(waitRun("first"), check.IsNil)
|
|
| 244 |
+ |
|
| 245 |
+ dockerCmd(c, "run", "-d", "--net=udlinkNet", "--name=second", "--link=first:foo", |
|
| 246 |
+ "busybox", "top") |
|
| 247 |
+ c.Assert(waitRun("second"), check.IsNil)
|
|
| 248 |
+ |
|
| 249 |
+ // ping to first and its alias foo must succeed |
|
| 250 |
+ _, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
|
|
| 251 |
+ c.Assert(err, check.IsNil) |
|
| 252 |
+ _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo")
|
|
| 253 |
+ c.Assert(err, check.IsNil) |
|
| 254 |
+ |
|
| 255 |
+ // Restart first container |
|
| 256 |
+ dockerCmd(c, "restart", "first") |
|
| 257 |
+ c.Assert(waitRun("first"), check.IsNil)
|
|
| 258 |
+ |
|
| 259 |
+ // ping to first and its alias foo must still succeed |
|
| 260 |
+ _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
|
|
| 261 |
+ c.Assert(err, check.IsNil) |
|
| 262 |
+ _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo")
|
|
| 263 |
+ c.Assert(err, check.IsNil) |
|
| 264 |
+ |
|
| 265 |
+ // Restart second container |
|
| 266 |
+ dockerCmd(c, "restart", "second") |
|
| 267 |
+ c.Assert(waitRun("second"), check.IsNil)
|
|
| 268 |
+ |
|
| 269 |
+ // ping to first and its alias foo must still succeed |
|
| 270 |
+ _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
|
|
| 271 |
+ c.Assert(err, check.IsNil) |
|
| 272 |
+ _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo")
|
|
| 273 |
+ c.Assert(err, check.IsNil) |
|
| 274 |
+} |
|
| 275 |
+ |
|
| 202 | 276 |
// Issue 9677. |
| 203 | 277 |
func (s *DockerSuite) TestRunWithDaemonFlags(c *check.C) {
|
| 204 | 278 |
out, _, err := dockerCmdWithError("--exec-opt", "foo=bar", "run", "-i", "busybox", "true")
|
| ... | ... |
@@ -48,10 +48,6 @@ func ValidateNetMode(c *container.Config, hc *container.HostConfig) error {
|
| 48 | 48 |
return ErrConflictContainerNetworkAndLinks |
| 49 | 49 |
} |
| 50 | 50 |
|
| 51 |
- if hc.NetworkMode.IsUserDefined() && len(hc.Links) > 0 {
|
|
| 52 |
- return ErrConflictUserDefinedNetworkAndLinks |
|
| 53 |
- } |
|
| 54 |
- |
|
| 55 | 51 |
if (hc.NetworkMode.IsHost() || hc.NetworkMode.IsContainer()) && len(hc.DNS) > 0 {
|
| 56 | 52 |
return ErrConflictNetworkAndDNS |
| 57 | 53 |
} |
| ... | ... |
@@ -409,11 +409,11 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host |
| 409 | 409 |
config.StdinOnce = true |
| 410 | 410 |
} |
| 411 | 411 |
|
| 412 |
- var networkingConfig *networktypes.NetworkingConfig |
|
| 412 |
+ networkingConfig := &networktypes.NetworkingConfig{
|
|
| 413 |
+ EndpointsConfig: make(map[string]*networktypes.EndpointSettings), |
|
| 414 |
+ } |
|
| 415 |
+ |
|
| 413 | 416 |
if *flIPv4Address != "" || *flIPv6Address != "" {
|
| 414 |
- networkingConfig = &networktypes.NetworkingConfig{
|
|
| 415 |
- EndpointsConfig: make(map[string]*networktypes.EndpointSettings), |
|
| 416 |
- } |
|
| 417 | 417 |
networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = &networktypes.EndpointSettings{
|
| 418 | 418 |
IPAMConfig: &networktypes.EndpointIPAMConfig{
|
| 419 | 419 |
IPv4Address: *flIPv4Address, |
| ... | ... |
@@ -422,6 +422,16 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host |
| 422 | 422 |
} |
| 423 | 423 |
} |
| 424 | 424 |
|
| 425 |
+ if hostConfig.NetworkMode.IsUserDefined() && len(hostConfig.Links) > 0 {
|
|
| 426 |
+ epConfig := networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] |
|
| 427 |
+ if epConfig == nil {
|
|
| 428 |
+ epConfig = &networktypes.EndpointSettings{}
|
|
| 429 |
+ } |
|
| 430 |
+ epConfig.Links = make([]string, len(hostConfig.Links)) |
|
| 431 |
+ copy(epConfig.Links, hostConfig.Links) |
|
| 432 |
+ networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = epConfig |
|
| 433 |
+ } |
|
| 434 |
+ |
|
| 425 | 435 |
return config, hostConfig, networkingConfig, cmd, nil |
| 426 | 436 |
} |
| 427 | 437 |
|