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 |
|