github.com/jthurman42/docker@v1.6.0-rc1/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  }