github.com/raychaser/docker@v1.5.0/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 runCmd := exec.Command(dockerBinary, "run", "--net=host", "busybox", "ls", "-la", "/etc/hosts") 19 out, _, _, err := runCommandWithStdoutStderr(runCmd) 20 if err != nil { 21 t.Fatal(out, err) 22 } 23 24 if !strings.HasPrefix(out, "-") { 25 t.Errorf("/etc/hosts should be a regular file") 26 } 27 28 deleteAllContainers() 29 30 logDone("link - /etc/hosts is a regular file") 31 } 32 33 func TestLinksEtcHostsContentMatch(t *testing.T) { 34 runCmd := exec.Command(dockerBinary, "run", "--net=host", "busybox", "cat", "/etc/hosts") 35 out, _, _, err := runCommandWithStdoutStderr(runCmd) 36 if err != nil { 37 t.Fatal(out, err) 38 } 39 40 hosts, err := ioutil.ReadFile("/etc/hosts") 41 if os.IsNotExist(err) { 42 t.Skip("/etc/hosts does not exist, skip this test") 43 } 44 45 if out != string(hosts) { 46 t.Errorf("container") 47 } 48 49 deleteAllContainers() 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 func TestLinksPingLinkedContainers(t *testing.T) { 68 var out string 69 out, _, _ = dockerCmd(t, "run", "-d", "--name", "container1", "busybox", "sleep", "10") 70 idA := stripTrailingCharacters(out) 71 out, _, _ = dockerCmd(t, "run", "-d", "--name", "container2", "busybox", "sleep", "10") 72 idB := stripTrailingCharacters(out) 73 dockerCmd(t, "run", "--rm", "--link", "container1:alias1", "--link", "container2:alias2", "busybox", "sh", "-c", "ping -c 1 alias1 -W 1 && ping -c 1 alias2 -W 1") 74 dockerCmd(t, "kill", idA) 75 dockerCmd(t, "kill", idB) 76 deleteAllContainers() 77 78 logDone("links - ping linked container") 79 } 80 81 func TestLinksPingLinkedContainersAfterRename(t *testing.T) { 82 out, _, _ := dockerCmd(t, "run", "-d", "--name", "container1", "busybox", "sleep", "10") 83 idA := stripTrailingCharacters(out) 84 out, _, _ = dockerCmd(t, "run", "-d", "--name", "container2", "busybox", "sleep", "10") 85 idB := stripTrailingCharacters(out) 86 dockerCmd(t, "rename", "container1", "container_new") 87 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") 88 dockerCmd(t, "kill", idA) 89 dockerCmd(t, "kill", idB) 90 deleteAllContainers() 91 92 logDone("links - ping linked container after rename") 93 } 94 95 func TestLinksPingLinkedContainersOnRename(t *testing.T) { 96 var out string 97 out, _, _ = dockerCmd(t, "run", "-d", "--name", "container1", "busybox", "sleep", "10") 98 idA := stripTrailingCharacters(out) 99 if idA == "" { 100 t.Fatal(out, "id should not be nil") 101 } 102 out, _, _ = dockerCmd(t, "run", "-d", "--link", "container1:alias1", "--name", "container2", "busybox", "sleep", "10") 103 idB := stripTrailingCharacters(out) 104 if idB == "" { 105 t.Fatal(out, "id should not be nil") 106 } 107 108 execCmd := exec.Command(dockerBinary, "exec", "container2", "ping", "-c", "1", "alias1", "-W", "1") 109 out, _, err := runCommandWithOutput(execCmd) 110 if err != nil { 111 t.Fatal(out, err) 112 } 113 114 dockerCmd(t, "rename", "container1", "container_new") 115 116 execCmd = exec.Command(dockerBinary, "exec", "container2", "ping", "-c", "1", "alias1", "-W", "1") 117 out, _, err = runCommandWithOutput(execCmd) 118 if err != nil { 119 t.Fatal(out, err) 120 } 121 122 deleteAllContainers() 123 124 logDone("links - ping linked container upon rename") 125 } 126 127 func TestLinksIpTablesRulesWhenLinkAndUnlink(t *testing.T) { 128 dockerCmd(t, "run", "-d", "--name", "child", "--publish", "8080:80", "busybox", "sleep", "10") 129 dockerCmd(t, "run", "-d", "--name", "parent", "--link", "child:http", "busybox", "sleep", "10") 130 131 childIP := findContainerIP(t, "child") 132 parentIP := findContainerIP(t, "parent") 133 134 sourceRule := []string{"DOCKER", "-i", "docker0", "-o", "docker0", "-p", "tcp", "-s", childIP, "--sport", "80", "-d", parentIP, "-j", "ACCEPT"} 135 destinationRule := []string{"DOCKER", "-i", "docker0", "-o", "docker0", "-p", "tcp", "-s", parentIP, "--dport", "80", "-d", childIP, "-j", "ACCEPT"} 136 if !iptables.Exists(sourceRule...) || !iptables.Exists(destinationRule...) { 137 t.Fatal("Iptables rules not found") 138 } 139 140 dockerCmd(t, "rm", "--link", "parent/http") 141 if iptables.Exists(sourceRule...) || iptables.Exists(destinationRule...) { 142 t.Fatal("Iptables rules should be removed when unlink") 143 } 144 145 dockerCmd(t, "kill", "child") 146 dockerCmd(t, "kill", "parent") 147 deleteAllContainers() 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 not failing on updating stopped parent links") 229 } 230 231 func TestLinksHostsFilesInject(t *testing.T) { 232 defer deleteAllContainers() 233 234 out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-itd", "--name", "one", "busybox", "top")) 235 if err != nil { 236 t.Fatal(err, out) 237 } 238 239 idOne := strings.TrimSpace(out) 240 241 out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "run", "-itd", "--name", "two", "--link", "one:onetwo", "busybox", "top")) 242 if err != nil { 243 t.Fatal(err, out) 244 } 245 246 idTwo := strings.TrimSpace(out) 247 248 time.Sleep(1 * time.Second) 249 250 contentOne, err := readContainerFile(idOne, "hosts") 251 if err != nil { 252 t.Fatal(err, string(contentOne)) 253 } 254 255 contentTwo, err := readContainerFile(idTwo, "hosts") 256 if err != nil { 257 t.Fatal(err, string(contentTwo)) 258 } 259 260 if !strings.Contains(string(contentTwo), "onetwo") { 261 t.Fatal("Host is not present in updated hosts file", string(contentTwo)) 262 } 263 264 logDone("link - ensure containers hosts files are updated with the link alias.") 265 } 266 267 func TestLinksNetworkHostContainer(t *testing.T) { 268 defer deleteAllContainers() 269 270 out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "--net", "host", "--name", "host_container", "busybox", "top")) 271 if err != nil { 272 t.Fatal(err, out) 273 } 274 275 out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "run", "--name", "should_fail", "--link", "host_container:tester", "busybox", "true")) 276 if err == nil || !strings.Contains(out, "--net=host can't be used with links. This would result in undefined behavior.") { 277 t.Fatalf("Running container linking to a container with --net host should have failed: %s", out) 278 } 279 280 logDone("link - error thrown when linking to container with --net host") 281 } 282 283 func TestLinksUpdateOnRestart(t *testing.T) { 284 defer deleteAllContainers() 285 286 if out, err := exec.Command(dockerBinary, "run", "-d", "--name", "one", "busybox", "top").CombinedOutput(); err != nil { 287 t.Fatal(err, string(out)) 288 } 289 out, err := exec.Command(dockerBinary, "run", "-d", "--name", "two", "--link", "one:onetwo", "--link", "one:one", "busybox", "top").CombinedOutput() 290 if err != nil { 291 t.Fatal(err, string(out)) 292 } 293 id := strings.TrimSpace(string(out)) 294 295 realIP, err := inspectField("one", "NetworkSettings.IPAddress") 296 if err != nil { 297 t.Fatal(err) 298 } 299 content, err := readContainerFile(id, "hosts") 300 if err != nil { 301 t.Fatal(err, string(content)) 302 } 303 getIP := func(hosts []byte, hostname string) string { 304 re := regexp.MustCompile(fmt.Sprintf(`(\S*)\t%s`, regexp.QuoteMeta(hostname))) 305 matches := re.FindSubmatch(hosts) 306 if matches == nil { 307 t.Fatalf("Hostname %s have no matches in hosts", hostname) 308 } 309 return string(matches[1]) 310 } 311 if ip := getIP(content, "one"); ip != realIP { 312 t.Fatalf("For 'one' alias expected IP: %s, got: %s", realIP, ip) 313 } 314 if ip := getIP(content, "onetwo"); ip != realIP { 315 t.Fatalf("For 'onetwo' alias expected IP: %s, got: %s", realIP, ip) 316 } 317 if out, err := exec.Command(dockerBinary, "restart", "one").CombinedOutput(); err != nil { 318 t.Fatal(err, string(out)) 319 } 320 realIP, err = inspectField("one", "NetworkSettings.IPAddress") 321 if err != nil { 322 t.Fatal(err) 323 } 324 content, err = readContainerFile(id, "hosts") 325 if err != nil { 326 t.Fatal(err, string(content)) 327 } 328 if ip := getIP(content, "one"); ip != realIP { 329 t.Fatalf("For 'one' alias expected IP: %s, got: %s", realIP, ip) 330 } 331 if ip := getIP(content, "onetwo"); ip != realIP { 332 t.Fatalf("For 'onetwo' alias expected IP: %s, got: %s", realIP, ip) 333 } 334 logDone("link - ensure containers hosts files are updated on restart") 335 }