github.com/grubernaut/docker@v1.6.0-rc2/integration-cli/docker_cli_links_test.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "os/exec" 8 "reflect" 9 "regexp" 10 "strings" 11 "testing" 12 "time" 13 14 "github.com/docker/docker/pkg/iptables" 15 ) 16 17 func TestLinksEtcHostsRegularFile(t *testing.T) { 18 defer deleteAllContainers() 19 20 runCmd := exec.Command(dockerBinary, "run", "--net=host", "busybox", "ls", "-la", "/etc/hosts") 21 out, _, _, err := runCommandWithStdoutStderr(runCmd) 22 if err != nil { 23 t.Fatal(out, err) 24 } 25 26 if !strings.HasPrefix(out, "-") { 27 t.Errorf("/etc/hosts should be a regular file") 28 } 29 logDone("link - /etc/hosts is a regular file") 30 } 31 32 func TestLinksEtcHostsContentMatch(t *testing.T) { 33 testRequires(t, SameHostDaemon) 34 defer deleteAllContainers() 35 36 runCmd := exec.Command(dockerBinary, "run", "--net=host", "busybox", "cat", "/etc/hosts") 37 out, _, _, err := runCommandWithStdoutStderr(runCmd) 38 if err != nil { 39 t.Fatal(out, err) 40 } 41 42 hosts, err := ioutil.ReadFile("/etc/hosts") 43 if os.IsNotExist(err) { 44 t.Skip("/etc/hosts does not exist, skip this test") 45 } 46 47 if out != string(hosts) { 48 t.Errorf("container") 49 } 50 51 logDone("link - /etc/hosts matches hosts copy") 52 } 53 54 func TestLinksPingUnlinkedContainers(t *testing.T) { 55 runCmd := exec.Command(dockerBinary, "run", "--rm", "busybox", "sh", "-c", "ping -c 1 alias1 -W 1 && ping -c 1 alias2 -W 1") 56 exitCode, err := runCommand(runCmd) 57 58 if exitCode == 0 { 59 t.Fatal("run ping did not fail") 60 } else if exitCode != 1 { 61 t.Fatalf("run ping failed with errors: %v", err) 62 } 63 64 logDone("links - ping unlinked container") 65 } 66 67 // Test for appropriate error when calling --link with an invalid target container 68 func TestLinksInvalidContainerTarget(t *testing.T) { 69 defer deleteAllContainers() 70 71 runCmd := exec.Command(dockerBinary, "run", "--link", "bogus:alias", "busybox", "true") 72 out, _, err := runCommandWithOutput(runCmd) 73 74 if err == nil { 75 t.Fatal("an invalid container target should produce an error") 76 } 77 if !strings.Contains(out, "Could not get container") { 78 t.Fatal("error output expected 'Could not get container', but got %q instead; err: %v", out, err) 79 } 80 81 logDone("links - linking to non-existent container should not work") 82 } 83 84 func TestLinksPingLinkedContainers(t *testing.T) { 85 defer deleteAllContainers() 86 87 runCmd := exec.Command(dockerBinary, "run", "-d", "--name", "container1", "--hostname", "fred", "busybox", "top") 88 if _, err := runCommand(runCmd); err != nil { 89 t.Fatal(err) 90 } 91 runCmd = exec.Command(dockerBinary, "run", "-d", "--name", "container2", "--hostname", "wilma", "busybox", "top") 92 if _, err := runCommand(runCmd); err != nil { 93 t.Fatal(err) 94 } 95 96 runArgs := []string{"run", "--rm", "--link", "container1:alias1", "--link", "container2:alias2", "busybox", "sh", "-c"} 97 pingCmd := "ping -c 1 %s -W 1 && ping -c 1 %s -W 1" 98 99 // test ping by alias, ping by name, and ping by hostname 100 // 1. Ping by alias 101 dockerCmd(t, append(runArgs, fmt.Sprintf(pingCmd, "alias1", "alias2"))...) 102 // 2. Ping by container name 103 dockerCmd(t, append(runArgs, fmt.Sprintf(pingCmd, "container1", "container2"))...) 104 // 3. Ping by hostname 105 dockerCmd(t, append(runArgs, fmt.Sprintf(pingCmd, "fred", "wilma"))...) 106 107 logDone("links - ping linked container") 108 } 109 110 func TestLinksPingLinkedContainersAfterRename(t *testing.T) { 111 defer deleteAllContainers() 112 113 out, _, _ := dockerCmd(t, "run", "-d", "--name", "container1", "busybox", "sleep", "10") 114 idA := stripTrailingCharacters(out) 115 out, _, _ = dockerCmd(t, "run", "-d", "--name", "container2", "busybox", "sleep", "10") 116 idB := stripTrailingCharacters(out) 117 dockerCmd(t, "rename", "container1", "container_new") 118 dockerCmd(t, "run", "--rm", "--link", "container_new:alias1", "--link", "container2:alias2", "busybox", "sh", "-c", "ping -c 1 alias1 -W 1 && ping -c 1 alias2 -W 1") 119 dockerCmd(t, "kill", idA) 120 dockerCmd(t, "kill", idB) 121 122 logDone("links - ping linked container after rename") 123 } 124 125 func TestLinksIpTablesRulesWhenLinkAndUnlink(t *testing.T) { 126 testRequires(t, SameHostDaemon) 127 defer deleteAllContainers() 128 129 dockerCmd(t, "run", "-d", "--name", "child", "--publish", "8080:80", "busybox", "sleep", "10") 130 dockerCmd(t, "run", "-d", "--name", "parent", "--link", "child:http", "busybox", "sleep", "10") 131 132 childIP := findContainerIP(t, "child") 133 parentIP := findContainerIP(t, "parent") 134 135 sourceRule := []string{"-i", "docker0", "-o", "docker0", "-p", "tcp", "-s", childIP, "--sport", "80", "-d", parentIP, "-j", "ACCEPT"} 136 destinationRule := []string{"-i", "docker0", "-o", "docker0", "-p", "tcp", "-s", parentIP, "--dport", "80", "-d", childIP, "-j", "ACCEPT"} 137 if !iptables.Exists("filter", "DOCKER", sourceRule...) || !iptables.Exists("filter", "DOCKER", destinationRule...) { 138 t.Fatal("Iptables rules not found") 139 } 140 141 dockerCmd(t, "rm", "--link", "parent/http") 142 if iptables.Exists("filter", "DOCKER", sourceRule...) || iptables.Exists("filter", "DOCKER", destinationRule...) { 143 t.Fatal("Iptables rules should be removed when unlink") 144 } 145 146 dockerCmd(t, "kill", "child") 147 dockerCmd(t, "kill", "parent") 148 149 logDone("link - verify iptables when link and unlink") 150 } 151 152 func TestLinksInspectLinksStarted(t *testing.T) { 153 var ( 154 expected = map[string]struct{}{"/container1:/testinspectlink/alias1": {}, "/container2:/testinspectlink/alias2": {}} 155 result []string 156 ) 157 defer deleteAllContainers() 158 dockerCmd(t, "run", "-d", "--name", "container1", "busybox", "sleep", "10") 159 dockerCmd(t, "run", "-d", "--name", "container2", "busybox", "sleep", "10") 160 dockerCmd(t, "run", "-d", "--name", "testinspectlink", "--link", "container1:alias1", "--link", "container2:alias2", "busybox", "sleep", "10") 161 links, err := inspectFieldJSON("testinspectlink", "HostConfig.Links") 162 if err != nil { 163 t.Fatal(err) 164 } 165 166 err = unmarshalJSON([]byte(links), &result) 167 if err != nil { 168 t.Fatal(err) 169 } 170 171 output := convertSliceOfStringsToMap(result) 172 173 equal := reflect.DeepEqual(output, expected) 174 175 if !equal { 176 t.Fatalf("Links %s, expected %s", result, expected) 177 } 178 logDone("link - links in started container inspect") 179 } 180 181 func TestLinksInspectLinksStopped(t *testing.T) { 182 var ( 183 expected = map[string]struct{}{"/container1:/testinspectlink/alias1": {}, "/container2:/testinspectlink/alias2": {}} 184 result []string 185 ) 186 defer deleteAllContainers() 187 dockerCmd(t, "run", "-d", "--name", "container1", "busybox", "sleep", "10") 188 dockerCmd(t, "run", "-d", "--name", "container2", "busybox", "sleep", "10") 189 dockerCmd(t, "run", "-d", "--name", "testinspectlink", "--link", "container1:alias1", "--link", "container2:alias2", "busybox", "true") 190 links, err := inspectFieldJSON("testinspectlink", "HostConfig.Links") 191 if err != nil { 192 t.Fatal(err) 193 } 194 195 err = unmarshalJSON([]byte(links), &result) 196 if err != nil { 197 t.Fatal(err) 198 } 199 200 output := convertSliceOfStringsToMap(result) 201 202 equal := reflect.DeepEqual(output, expected) 203 204 if !equal { 205 t.Fatalf("Links %s, but expected %s", result, expected) 206 } 207 208 logDone("link - links in stopped container inspect") 209 } 210 211 func TestLinksNotStartedParentNotFail(t *testing.T) { 212 defer deleteAllContainers() 213 runCmd := exec.Command(dockerBinary, "create", "--name=first", "busybox", "top") 214 out, _, _, err := runCommandWithStdoutStderr(runCmd) 215 if err != nil { 216 t.Fatal(out, err) 217 } 218 runCmd = exec.Command(dockerBinary, "create", "--name=second", "--link=first:first", "busybox", "top") 219 out, _, _, err = runCommandWithStdoutStderr(runCmd) 220 if err != nil { 221 t.Fatal(out, err) 222 } 223 runCmd = exec.Command(dockerBinary, "start", "first") 224 out, _, _, err = runCommandWithStdoutStderr(runCmd) 225 if err != nil { 226 t.Fatal(out, err) 227 } 228 logDone("link - container start successfully updating stopped parent links") 229 } 230 231 func TestLinksHostsFilesInject(t *testing.T) { 232 testRequires(t, SameHostDaemon, ExecSupport) 233 234 defer deleteAllContainers() 235 236 out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-itd", "--name", "one", "busybox", "top")) 237 if err != nil { 238 t.Fatal(err, out) 239 } 240 241 idOne := strings.TrimSpace(out) 242 243 out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "run", "-itd", "--name", "two", "--link", "one:onetwo", "busybox", "top")) 244 if err != nil { 245 t.Fatal(err, out) 246 } 247 248 idTwo := strings.TrimSpace(out) 249 250 time.Sleep(1 * time.Second) 251 252 contentOne, err := readContainerFileWithExec(idOne, "/etc/hosts") 253 if err != nil { 254 t.Fatal(err, string(contentOne)) 255 } 256 257 contentTwo, err := readContainerFileWithExec(idTwo, "/etc/hosts") 258 if err != nil { 259 t.Fatal(err, string(contentTwo)) 260 } 261 262 if !strings.Contains(string(contentTwo), "onetwo") { 263 t.Fatal("Host is not present in updated hosts file", string(contentTwo)) 264 } 265 266 logDone("link - ensure containers hosts files are updated with the link alias.") 267 } 268 269 func TestLinksNetworkHostContainer(t *testing.T) { 270 defer deleteAllContainers() 271 272 out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "--net", "host", "--name", "host_container", "busybox", "top")) 273 if err != nil { 274 t.Fatal(err, out) 275 } 276 277 out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "run", "--name", "should_fail", "--link", "host_container:tester", "busybox", "true")) 278 if err == nil || !strings.Contains(out, "--net=host can't be used with links. This would result in undefined behavior.") { 279 t.Fatalf("Running container linking to a container with --net host should have failed: %s", out) 280 } 281 282 logDone("link - error thrown when linking to container with --net host") 283 } 284 285 func TestLinksUpdateOnRestart(t *testing.T) { 286 testRequires(t, SameHostDaemon, ExecSupport) 287 288 defer deleteAllContainers() 289 290 if out, err := exec.Command(dockerBinary, "run", "-d", "--name", "one", "busybox", "top").CombinedOutput(); err != nil { 291 t.Fatal(err, string(out)) 292 } 293 out, err := exec.Command(dockerBinary, "run", "-d", "--name", "two", "--link", "one:onetwo", "--link", "one:one", "busybox", "top").CombinedOutput() 294 if err != nil { 295 t.Fatal(err, string(out)) 296 } 297 id := strings.TrimSpace(string(out)) 298 299 realIP, err := inspectField("one", "NetworkSettings.IPAddress") 300 if err != nil { 301 t.Fatal(err) 302 } 303 content, err := readContainerFileWithExec(id, "/etc/hosts") 304 if err != nil { 305 t.Fatal(err, string(content)) 306 } 307 getIP := func(hosts []byte, hostname string) string { 308 re := regexp.MustCompile(fmt.Sprintf(`(\S*)\t%s`, regexp.QuoteMeta(hostname))) 309 matches := re.FindSubmatch(hosts) 310 if matches == nil { 311 t.Fatalf("Hostname %s have no matches in hosts", hostname) 312 } 313 return string(matches[1]) 314 } 315 if ip := getIP(content, "one"); ip != realIP { 316 t.Fatalf("For 'one' alias expected IP: %s, got: %s", realIP, ip) 317 } 318 if ip := getIP(content, "onetwo"); ip != realIP { 319 t.Fatalf("For 'onetwo' alias expected IP: %s, got: %s", realIP, ip) 320 } 321 if out, err := exec.Command(dockerBinary, "restart", "one").CombinedOutput(); err != nil { 322 t.Fatal(err, string(out)) 323 } 324 realIP, err = inspectField("one", "NetworkSettings.IPAddress") 325 if err != nil { 326 t.Fatal(err) 327 } 328 content, err = readContainerFileWithExec(id, "/etc/hosts") 329 if err != nil { 330 t.Fatal(err, string(content)) 331 } 332 if ip := getIP(content, "one"); ip != realIP { 333 t.Fatalf("For 'one' alias expected IP: %s, got: %s", realIP, ip) 334 } 335 if ip := getIP(content, "onetwo"); ip != realIP { 336 t.Fatalf("For 'onetwo' alias expected IP: %s, got: %s", realIP, ip) 337 } 338 logDone("link - ensure containers hosts files are updated on restart") 339 }