mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00

Shout out to @estesp for the idea. Some use cases of `readContainerFile` can be replaced with `docker exec $id cat $file`. This helper method can eliminate the requirement that host/cli should be on the same machine. TestRunMutableNetworkFiles and TestRunResolvconfUpdater still need to access the docker host filesystem as they modify the file directly from there assuming cli and daemon are on the same machine. This fixes TestLinksUpdateOnRestart and TestLinksHostsFilesInject for Windows CI. Signed-off-by: Ahmet Alp Balkan <ahmetalpbalkan@gmail.com>
341 lines
11 KiB
Go
341 lines
11 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"reflect"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/docker/docker/pkg/iptables"
|
|
)
|
|
|
|
func TestLinksEtcHostsRegularFile(t *testing.T) {
|
|
defer deleteAllContainers()
|
|
|
|
runCmd := exec.Command(dockerBinary, "run", "--net=host", "busybox", "ls", "-la", "/etc/hosts")
|
|
out, _, _, err := runCommandWithStdoutStderr(runCmd)
|
|
if err != nil {
|
|
t.Fatal(out, err)
|
|
}
|
|
|
|
if !strings.HasPrefix(out, "-") {
|
|
t.Errorf("/etc/hosts should be a regular file")
|
|
}
|
|
logDone("link - /etc/hosts is a regular file")
|
|
}
|
|
|
|
func TestLinksEtcHostsContentMatch(t *testing.T) {
|
|
defer deleteAllContainers()
|
|
|
|
testRequires(t, SameHostDaemon)
|
|
|
|
runCmd := exec.Command(dockerBinary, "run", "--net=host", "busybox", "cat", "/etc/hosts")
|
|
out, _, _, err := runCommandWithStdoutStderr(runCmd)
|
|
if err != nil {
|
|
t.Fatal(out, err)
|
|
}
|
|
|
|
hosts, err := ioutil.ReadFile("/etc/hosts")
|
|
if os.IsNotExist(err) {
|
|
t.Skip("/etc/hosts does not exist, skip this test")
|
|
}
|
|
|
|
if out != string(hosts) {
|
|
t.Errorf("container")
|
|
}
|
|
|
|
logDone("link - /etc/hosts matches hosts copy")
|
|
}
|
|
|
|
func TestLinksPingUnlinkedContainers(t *testing.T) {
|
|
runCmd := exec.Command(dockerBinary, "run", "--rm", "busybox", "sh", "-c", "ping -c 1 alias1 -W 1 && ping -c 1 alias2 -W 1")
|
|
exitCode, err := runCommand(runCmd)
|
|
|
|
if exitCode == 0 {
|
|
t.Fatal("run ping did not fail")
|
|
} else if exitCode != 1 {
|
|
t.Fatalf("run ping failed with errors: %v", err)
|
|
}
|
|
|
|
logDone("links - ping unlinked container")
|
|
}
|
|
|
|
// Test for appropriate error when calling --link with an invalid target container
|
|
func TestLinksInvalidContainerTarget(t *testing.T) {
|
|
defer deleteAllContainers()
|
|
|
|
runCmd := exec.Command(dockerBinary, "run", "--link", "bogus:alias", "busybox", "true")
|
|
out, _, err := runCommandWithOutput(runCmd)
|
|
|
|
if err == nil {
|
|
t.Fatal("an invalid container target should produce an error")
|
|
}
|
|
if !strings.Contains(out, "Could not get container") {
|
|
t.Fatal("error output expected 'Could not get container', but got %q instead; err: %v", out, err)
|
|
}
|
|
|
|
logDone("links - linking to non-existent container should not work")
|
|
}
|
|
|
|
func TestLinksPingLinkedContainers(t *testing.T) {
|
|
defer deleteAllContainers()
|
|
|
|
runCmd := exec.Command(dockerBinary, "run", "-d", "--name", "container1", "--hostname", "fred", "busybox", "top")
|
|
if _, err := runCommand(runCmd); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
runCmd = exec.Command(dockerBinary, "run", "-d", "--name", "container2", "--hostname", "wilma", "busybox", "top")
|
|
if _, err := runCommand(runCmd); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
runArgs := []string{"run", "--rm", "--link", "container1:alias1", "--link", "container2:alias2", "busybox", "sh", "-c"}
|
|
pingCmd := "ping -c 1 %s -W 1 && ping -c 1 %s -W 1"
|
|
|
|
// test ping by alias, ping by name, and ping by hostname
|
|
// 1. Ping by alias
|
|
dockerCmd(t, append(runArgs, fmt.Sprintf(pingCmd, "alias1", "alias2"))...)
|
|
// 2. Ping by container name
|
|
dockerCmd(t, append(runArgs, fmt.Sprintf(pingCmd, "container1", "container2"))...)
|
|
// 3. Ping by hostname
|
|
dockerCmd(t, append(runArgs, fmt.Sprintf(pingCmd, "fred", "wilma"))...)
|
|
|
|
logDone("links - ping linked container")
|
|
}
|
|
|
|
func TestLinksPingLinkedContainersAfterRename(t *testing.T) {
|
|
defer deleteAllContainers()
|
|
|
|
out, _, _ := dockerCmd(t, "run", "-d", "--name", "container1", "busybox", "sleep", "10")
|
|
idA := stripTrailingCharacters(out)
|
|
out, _, _ = dockerCmd(t, "run", "-d", "--name", "container2", "busybox", "sleep", "10")
|
|
idB := stripTrailingCharacters(out)
|
|
dockerCmd(t, "rename", "container1", "container_new")
|
|
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")
|
|
dockerCmd(t, "kill", idA)
|
|
dockerCmd(t, "kill", idB)
|
|
|
|
logDone("links - ping linked container after rename")
|
|
}
|
|
|
|
func TestLinksIpTablesRulesWhenLinkAndUnlink(t *testing.T) {
|
|
defer deleteAllContainers()
|
|
|
|
testRequires(t, SameHostDaemon)
|
|
|
|
dockerCmd(t, "run", "-d", "--name", "child", "--publish", "8080:80", "busybox", "sleep", "10")
|
|
dockerCmd(t, "run", "-d", "--name", "parent", "--link", "child:http", "busybox", "sleep", "10")
|
|
|
|
childIP := findContainerIP(t, "child")
|
|
parentIP := findContainerIP(t, "parent")
|
|
|
|
sourceRule := []string{"DOCKER", "-i", "docker0", "-o", "docker0", "-p", "tcp", "-s", childIP, "--sport", "80", "-d", parentIP, "-j", "ACCEPT"}
|
|
destinationRule := []string{"DOCKER", "-i", "docker0", "-o", "docker0", "-p", "tcp", "-s", parentIP, "--dport", "80", "-d", childIP, "-j", "ACCEPT"}
|
|
if !iptables.Exists(sourceRule...) || !iptables.Exists(destinationRule...) {
|
|
t.Fatal("Iptables rules not found")
|
|
}
|
|
|
|
dockerCmd(t, "rm", "--link", "parent/http")
|
|
if iptables.Exists(sourceRule...) || iptables.Exists(destinationRule...) {
|
|
t.Fatal("Iptables rules should be removed when unlink")
|
|
}
|
|
|
|
dockerCmd(t, "kill", "child")
|
|
dockerCmd(t, "kill", "parent")
|
|
|
|
logDone("link - verify iptables when link and unlink")
|
|
}
|
|
|
|
func TestLinksInspectLinksStarted(t *testing.T) {
|
|
var (
|
|
expected = map[string]struct{}{"/container1:/testinspectlink/alias1": {}, "/container2:/testinspectlink/alias2": {}}
|
|
result []string
|
|
)
|
|
defer deleteAllContainers()
|
|
dockerCmd(t, "run", "-d", "--name", "container1", "busybox", "sleep", "10")
|
|
dockerCmd(t, "run", "-d", "--name", "container2", "busybox", "sleep", "10")
|
|
dockerCmd(t, "run", "-d", "--name", "testinspectlink", "--link", "container1:alias1", "--link", "container2:alias2", "busybox", "sleep", "10")
|
|
links, err := inspectFieldJSON("testinspectlink", "HostConfig.Links")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
err = unmarshalJSON([]byte(links), &result)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
output := convertSliceOfStringsToMap(result)
|
|
|
|
equal := reflect.DeepEqual(output, expected)
|
|
|
|
if !equal {
|
|
t.Fatalf("Links %s, expected %s", result, expected)
|
|
}
|
|
logDone("link - links in started container inspect")
|
|
}
|
|
|
|
func TestLinksInspectLinksStopped(t *testing.T) {
|
|
var (
|
|
expected = map[string]struct{}{"/container1:/testinspectlink/alias1": {}, "/container2:/testinspectlink/alias2": {}}
|
|
result []string
|
|
)
|
|
defer deleteAllContainers()
|
|
dockerCmd(t, "run", "-d", "--name", "container1", "busybox", "sleep", "10")
|
|
dockerCmd(t, "run", "-d", "--name", "container2", "busybox", "sleep", "10")
|
|
dockerCmd(t, "run", "-d", "--name", "testinspectlink", "--link", "container1:alias1", "--link", "container2:alias2", "busybox", "true")
|
|
links, err := inspectFieldJSON("testinspectlink", "HostConfig.Links")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
err = unmarshalJSON([]byte(links), &result)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
output := convertSliceOfStringsToMap(result)
|
|
|
|
equal := reflect.DeepEqual(output, expected)
|
|
|
|
if !equal {
|
|
t.Fatalf("Links %s, but expected %s", result, expected)
|
|
}
|
|
|
|
logDone("link - links in stopped container inspect")
|
|
}
|
|
|
|
func TestLinksNotStartedParentNotFail(t *testing.T) {
|
|
defer deleteAllContainers()
|
|
runCmd := exec.Command(dockerBinary, "create", "--name=first", "busybox", "top")
|
|
out, _, _, err := runCommandWithStdoutStderr(runCmd)
|
|
if err != nil {
|
|
t.Fatal(out, err)
|
|
}
|
|
runCmd = exec.Command(dockerBinary, "create", "--name=second", "--link=first:first", "busybox", "top")
|
|
out, _, _, err = runCommandWithStdoutStderr(runCmd)
|
|
if err != nil {
|
|
t.Fatal(out, err)
|
|
}
|
|
runCmd = exec.Command(dockerBinary, "start", "first")
|
|
out, _, _, err = runCommandWithStdoutStderr(runCmd)
|
|
if err != nil {
|
|
t.Fatal(out, err)
|
|
}
|
|
logDone("link - container start successfully updating stopped parent links")
|
|
}
|
|
|
|
func TestLinksHostsFilesInject(t *testing.T) {
|
|
testRequires(t, SameHostDaemon)
|
|
|
|
defer deleteAllContainers()
|
|
|
|
out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-itd", "--name", "one", "busybox", "top"))
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
|
|
idOne := strings.TrimSpace(out)
|
|
|
|
out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "run", "-itd", "--name", "two", "--link", "one:onetwo", "busybox", "top"))
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
|
|
idTwo := strings.TrimSpace(out)
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
contentOne, err := readContainerFileWithExec(idOne, "/etc/hosts")
|
|
if err != nil {
|
|
t.Fatal(err, string(contentOne))
|
|
}
|
|
|
|
contentTwo, err := readContainerFileWithExec(idTwo, "/etc/hosts")
|
|
if err != nil {
|
|
t.Fatal(err, string(contentTwo))
|
|
}
|
|
|
|
if !strings.Contains(string(contentTwo), "onetwo") {
|
|
t.Fatal("Host is not present in updated hosts file", string(contentTwo))
|
|
}
|
|
|
|
logDone("link - ensure containers hosts files are updated with the link alias.")
|
|
}
|
|
|
|
func TestLinksNetworkHostContainer(t *testing.T) {
|
|
defer deleteAllContainers()
|
|
|
|
out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "--net", "host", "--name", "host_container", "busybox", "top"))
|
|
if err != nil {
|
|
t.Fatal(err, out)
|
|
}
|
|
|
|
out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "run", "--name", "should_fail", "--link", "host_container:tester", "busybox", "true"))
|
|
if err == nil || !strings.Contains(out, "--net=host can't be used with links. This would result in undefined behavior.") {
|
|
t.Fatalf("Running container linking to a container with --net host should have failed: %s", out)
|
|
}
|
|
|
|
logDone("link - error thrown when linking to container with --net host")
|
|
}
|
|
|
|
func TestLinksUpdateOnRestart(t *testing.T) {
|
|
testRequires(t, SameHostDaemon)
|
|
|
|
defer deleteAllContainers()
|
|
|
|
if out, err := exec.Command(dockerBinary, "run", "-d", "--name", "one", "busybox", "top").CombinedOutput(); err != nil {
|
|
t.Fatal(err, string(out))
|
|
}
|
|
out, err := exec.Command(dockerBinary, "run", "-d", "--name", "two", "--link", "one:onetwo", "--link", "one:one", "busybox", "top").CombinedOutput()
|
|
if err != nil {
|
|
t.Fatal(err, string(out))
|
|
}
|
|
id := strings.TrimSpace(string(out))
|
|
|
|
realIP, err := inspectField("one", "NetworkSettings.IPAddress")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
content, err := readContainerFileWithExec(id, "/etc/hosts")
|
|
if err != nil {
|
|
t.Fatal(err, string(content))
|
|
}
|
|
getIP := func(hosts []byte, hostname string) string {
|
|
re := regexp.MustCompile(fmt.Sprintf(`(\S*)\t%s`, regexp.QuoteMeta(hostname)))
|
|
matches := re.FindSubmatch(hosts)
|
|
if matches == nil {
|
|
t.Fatalf("Hostname %s have no matches in hosts", hostname)
|
|
}
|
|
return string(matches[1])
|
|
}
|
|
if ip := getIP(content, "one"); ip != realIP {
|
|
t.Fatalf("For 'one' alias expected IP: %s, got: %s", realIP, ip)
|
|
}
|
|
if ip := getIP(content, "onetwo"); ip != realIP {
|
|
t.Fatalf("For 'onetwo' alias expected IP: %s, got: %s", realIP, ip)
|
|
}
|
|
if out, err := exec.Command(dockerBinary, "restart", "one").CombinedOutput(); err != nil {
|
|
t.Fatal(err, string(out))
|
|
}
|
|
realIP, err = inspectField("one", "NetworkSettings.IPAddress")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
content, err = readContainerFileWithExec(id, "/etc/hosts")
|
|
if err != nil {
|
|
t.Fatal(err, string(content))
|
|
}
|
|
if ip := getIP(content, "one"); ip != realIP {
|
|
t.Fatalf("For 'one' alias expected IP: %s, got: %s", realIP, ip)
|
|
}
|
|
if ip := getIP(content, "onetwo"); ip != realIP {
|
|
t.Fatalf("For 'onetwo' alias expected IP: %s, got: %s", realIP, ip)
|
|
}
|
|
logDone("link - ensure containers hosts files are updated on restart")
|
|
}
|