Browse code

Allow links to be specified with only the name if this matches the alias

Signed-off-by: Antonio Murdaca <me@runcom.ninja>

Antonio Murdaca authored on 2015/05/08 05:02:14
Showing 11 changed files
... ...
@@ -686,14 +686,14 @@ func (daemon *Daemon) RegisterLink(parent, child *Container, alias string) error
686 686
 func (daemon *Daemon) RegisterLinks(container *Container, hostConfig *runconfig.HostConfig) error {
687 687
 	if hostConfig != nil && hostConfig.Links != nil {
688 688
 		for _, l := range hostConfig.Links {
689
-			parts, err := parsers.PartParser("name:alias", l)
689
+			name, alias, err := parsers.ParseLink(l)
690 690
 			if err != nil {
691 691
 				return err
692 692
 			}
693
-			child, err := daemon.Get(parts["name"])
693
+			child, err := daemon.Get(name)
694 694
 			if err != nil {
695 695
 				//An error from daemon.Get() means this name could not be found
696
-				return fmt.Errorf("Could not get container for %s", parts["name"])
696
+				return fmt.Errorf("Could not get container for %s", name)
697 697
 			}
698 698
 			for child.hostConfig.NetworkMode.IsContainer() {
699 699
 				parts := strings.SplitN(string(child.hostConfig.NetworkMode), ":", 2)
... ...
@@ -705,7 +705,7 @@ func (daemon *Daemon) RegisterLinks(container *Container, hostConfig *runconfig.
705 705
 			if child.hostConfig.NetworkMode.IsHost() {
706 706
 				return runconfig.ErrConflictHostNetworkAndLinks
707 707
 			}
708
-			if err := daemon.RegisterLink(container, child, parts["alias"]); err != nil {
708
+			if err := daemon.RegisterLink(container, child, alias); err != nil {
709 709
 				return err
710 710
 			}
711 711
 		}
... ...
@@ -133,7 +133,8 @@ two memory nodes.
133 133
    Read labels from a file. Delimit each label with an EOL.
134 134
 
135 135
 **--link**=[]
136
-   Add link to another container in the form of <name or id>:alias
136
+   Add link to another container in the form of <name or id>:alias or just
137
+   <name or id> in which case the alias will match the name.
137 138
 
138 139
 **--lxc-conf**=[]
139 140
    (lxc exec-driver only) Add custom lxc options --lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"
... ...
@@ -232,7 +232,8 @@ ENTRYPOINT.
232 232
    Read in a line delimited file of labels
233 233
 
234 234
 **--link**=[]
235
-   Add link to another container in the form of <name or id>:alias
235
+   Add link to another container in the form of <name or id>:alias or just <name or id>
236
+in which case the alias will match the name
236 237
 
237 238
    If the operator
238 239
 uses **--link** when starting the new client container, then the client
... ...
@@ -2136,6 +2136,12 @@ Guide.
2136 2136
 The `--link` flag will link the container named `/redis` into the newly
2137 2137
 created container with the alias `redis`. The new container can access the
2138 2138
 network and environment of the `redis` container via environment variables.
2139
+The `--link` flag will also just accept the form `<name or id>` in which case
2140
+the alias will match the name. For instance, you could have written the previous
2141
+example as:
2142
+
2143
+    $ docker run --link redis --name console ubuntu bash
2144
+
2139 2145
 The `--name` flag will assign the name `console` to the newly created
2140 2146
 container.
2141 2147
 
... ...
@@ -927,7 +927,7 @@ or override the Dockerfile's exposed defaults:
927 927
                    Both hostPort and containerPort can be specified as a range of ports. 
928 928
                    When specifying ranges for both, the number of container ports in the range must match the number of host ports in the range. (e.g., `-p 1234-1236:1234-1236/tcp`)
929 929
                    (use 'docker port' to see the actual mapping)
930
-    --link=""  : Add link to another container (<name or id>:alias)
930
+    --link=""  : Add link to another container (<name or id>:alias or <name or id>)
931 931
 
932 932
 As mentioned previously, `EXPOSE` (and `--expose`) makes ports available
933 933
 **in** a container for incoming connections. The port number on the
... ...
@@ -114,7 +114,7 @@ You can also use `docker inspect` to return the container's name.
114 114
     $ docker inspect -f "{{ .Name }}" aed84ee21bde
115 115
     /web
116 116
 
117
-> **Note:** 
117
+> **Note:**
118 118
 > Container names have to be unique. That means you can only call
119 119
 > one container `web`. If you want to re-use a container name you must delete
120 120
 > the old container (with `docker rm`) before you can create a new
... ...
@@ -151,6 +151,14 @@ earlier. The `--link` flag takes the form:
151 151
 
152 152
 Where `name` is the name of the container we're linking to and `alias` is an
153 153
 alias for the link name. You'll see how that alias gets used shortly.
154
+The `--link` flag also takes the form:
155
+
156
+	--link <name or id>
157
+
158
+In which case the alias will match the name. You could have written the previous
159
+example as:
160
+
161
+    $ docker run -d -P --name web --link db training/webapp python app.py
154 162
 
155 163
 Next, inspect your linked containers with `docker inspect`:
156 164
 
... ...
@@ -15,7 +15,6 @@ import (
15 15
 )
16 16
 
17 17
 func (s *DockerSuite) TestLinksEtcHostsRegularFile(c *check.C) {
18
-
19 18
 	runCmd := exec.Command(dockerBinary, "run", "--net=host", "busybox", "ls", "-la", "/etc/hosts")
20 19
 	out, _, _, err := runCommandWithStdoutStderr(runCmd)
21 20
 	if err != nil {
... ...
@@ -331,3 +330,23 @@ func (s *DockerSuite) TestLinksEnvs(c *check.C) {
331 331
 		c.Fatalf("Incorrect output: %s", out)
332 332
 	}
333 333
 }
334
+
335
+func (s *DockerSuite) TestLinkShortDefinition(c *check.C) {
336
+	runCmd := exec.Command(dockerBinary, "run", "-d", "--name", "shortlinkdef", "busybox", "top")
337
+	out, _, err := runCommandWithOutput(runCmd)
338
+	c.Assert(err, check.IsNil)
339
+
340
+	cid := strings.TrimSpace(out)
341
+	c.Assert(waitRun(cid), check.IsNil)
342
+
343
+	runCmd = exec.Command(dockerBinary, "run", "-d", "--name", "link2", "--link", "shortlinkdef", "busybox", "top")
344
+	out, _, err = runCommandWithOutput(runCmd)
345
+	c.Assert(err, check.IsNil)
346
+
347
+	cid2 := strings.TrimSpace(out)
348
+	c.Assert(waitRun(cid2), check.IsNil)
349
+
350
+	links, err := inspectFieldJSON(cid2, "HostConfig.Links")
351
+	c.Assert(err, check.IsNil)
352
+	c.Assert(links, check.Equals, "[\"/shortlinkdef:/link2/shortlinkdef\"]")
353
+}
... ...
@@ -196,7 +196,7 @@ func ValidateAttach(val string) (string, error) {
196 196
 }
197 197
 
198 198
 func ValidateLink(val string) (string, error) {
199
-	if _, err := parsers.PartParser("name:alias", val); err != nil {
199
+	if _, _, err := parsers.ParseLink(val); err != nil {
200 200
 		return val, err
201 201
 	}
202 202
 	return val, nil
... ...
@@ -135,3 +135,17 @@ func ParsePortRange(ports string) (uint64, uint64, error) {
135 135
 	}
136 136
 	return start, end, nil
137 137
 }
138
+
139
+func ParseLink(val string) (string, string, error) {
140
+	if val == "" {
141
+		return "", "", fmt.Errorf("empty string specified for links")
142
+	}
143
+	arr := strings.Split(val, ":")
144
+	if len(arr) > 2 {
145
+		return "", "", fmt.Errorf("bad format for links: %s", val)
146
+	}
147
+	if len(arr) == 1 {
148
+		return val, val, nil
149
+	}
150
+	return arr[0], arr[1], nil
151
+}
... ...
@@ -123,3 +123,35 @@ func TestParsePortRangeIncorrectStartRange(t *testing.T) {
123 123
 		t.Fatalf("Expecting error 'Invalid range specified for the Port' but received %s.", err)
124 124
 	}
125 125
 }
126
+
127
+func TestParseLink(t *testing.T) {
128
+	name, alias, err := ParseLink("name:alias")
129
+	if err != nil {
130
+		t.Fatalf("Expected not to error out on a valid name:alias format but got: %v", err)
131
+	}
132
+	if name != "name" {
133
+		t.Fatalf("Link name should have been name, got %s instead", name)
134
+	}
135
+	if alias != "alias" {
136
+		t.Fatalf("Link alias should have been alias, got %s instead", alias)
137
+	}
138
+	// short format definition
139
+	name, alias, err = ParseLink("name")
140
+	if err != nil {
141
+		t.Fatalf("Expected not to error out on a valid name only format but got: %v", err)
142
+	}
143
+	if name != "name" {
144
+		t.Fatalf("Link name should have been name, got %s instead", name)
145
+	}
146
+	if alias != "name" {
147
+		t.Fatalf("Link alias should have been name, got %s instead", alias)
148
+	}
149
+	// empty string link definition is not allowed
150
+	if _, _, err := ParseLink(""); err == nil || !strings.Contains(err.Error(), "empty string specified for links") {
151
+		t.Fatalf("Expected error 'empty string specified for links' but got: %v", err)
152
+	}
153
+	// more than two colons are not allowed
154
+	if _, _, err := ParseLink("link:alias:wrong"); err == nil || !strings.Contains(err.Error(), "bad format for links: link:alias:wrong") {
155
+		t.Fatalf("Expected error 'bad format for links: link:alias:wrong' but got: %v", err)
156
+	}
157
+}
... ...
@@ -45,13 +45,6 @@ func TestParseRunLinks(t *testing.T) {
45 45
 	if _, hostConfig := mustParse(t, ""); len(hostConfig.Links) != 0 {
46 46
 		t.Fatalf("Error parsing links. No link expected, received: %v", hostConfig.Links)
47 47
 	}
48
-
49
-	if _, _, err := parse(t, "--link a"); err == nil {
50
-		t.Fatalf("Error parsing links. `--link a` should be an error but is not")
51
-	}
52
-	if _, _, err := parse(t, "--link"); err == nil {
53
-		t.Fatalf("Error parsing links. `--link` should be an error but is not")
54
-	}
55 48
 }
56 49
 
57 50
 func TestParseRunAttach(t *testing.T) {