github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/integration-cli/docker_cli_port_test.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"regexp"
     7  	"sort"
     8  	"strconv"
     9  	"strings"
    10  	"testing"
    11  
    12  	"gotest.tools/v3/assert"
    13  )
    14  
    15  func (s *DockerSuite) TestPortList(c *testing.T) {
    16  	testRequires(c, DaemonIsLinux)
    17  	// one port
    18  	out, _ := dockerCmd(c, "run", "-d", "-p", "9876:80", "busybox", "top")
    19  	firstID := strings.TrimSpace(out)
    20  
    21  	out, _ = dockerCmd(c, "port", firstID, "80")
    22  
    23  	err := assertPortList(c, out, []string{"0.0.0.0:9876"})
    24  	// Port list is not correct
    25  	assert.NilError(c, err)
    26  
    27  	out, _ = dockerCmd(c, "port", firstID)
    28  
    29  	err = assertPortList(c, out, []string{"80/tcp -> 0.0.0.0:9876"})
    30  	// Port list is not correct
    31  	assert.NilError(c, err)
    32  
    33  	dockerCmd(c, "rm", "-f", firstID)
    34  
    35  	// three port
    36  	out, _ = dockerCmd(c, "run", "-d",
    37  		"-p", "9876:80",
    38  		"-p", "9877:81",
    39  		"-p", "9878:82",
    40  		"busybox", "top")
    41  	ID := strings.TrimSpace(out)
    42  
    43  	out, _ = dockerCmd(c, "port", ID, "80")
    44  
    45  	err = assertPortList(c, out, []string{"0.0.0.0:9876"})
    46  	// Port list is not correct
    47  	assert.NilError(c, err)
    48  
    49  	out, _ = dockerCmd(c, "port", ID)
    50  
    51  	err = assertPortList(c, out, []string{
    52  		"80/tcp -> 0.0.0.0:9876",
    53  		"81/tcp -> 0.0.0.0:9877",
    54  		"82/tcp -> 0.0.0.0:9878"})
    55  	// Port list is not correct
    56  	assert.NilError(c, err)
    57  
    58  	dockerCmd(c, "rm", "-f", ID)
    59  
    60  	// more and one port mapped to the same container port
    61  	out, _ = dockerCmd(c, "run", "-d",
    62  		"-p", "9876:80",
    63  		"-p", "9999:80",
    64  		"-p", "9877:81",
    65  		"-p", "9878:82",
    66  		"busybox", "top")
    67  	ID = strings.TrimSpace(out)
    68  
    69  	out, _ = dockerCmd(c, "port", ID, "80")
    70  
    71  	err = assertPortList(c, out, []string{"0.0.0.0:9876", "0.0.0.0:9999"})
    72  	// Port list is not correct
    73  	assert.NilError(c, err)
    74  
    75  	out, _ = dockerCmd(c, "port", ID)
    76  
    77  	err = assertPortList(c, out, []string{
    78  		"80/tcp -> 0.0.0.0:9876",
    79  		"80/tcp -> 0.0.0.0:9999",
    80  		"81/tcp -> 0.0.0.0:9877",
    81  		"82/tcp -> 0.0.0.0:9878"})
    82  	// Port list is not correct
    83  	assert.NilError(c, err)
    84  	dockerCmd(c, "rm", "-f", ID)
    85  
    86  	testRange := func() {
    87  		// host port ranges used
    88  		IDs := make([]string, 3)
    89  		for i := 0; i < 3; i++ {
    90  			out, _ = dockerCmd(c, "run", "-d",
    91  				"-p", "9090-9092:80",
    92  				"busybox", "top")
    93  			IDs[i] = strings.TrimSpace(out)
    94  
    95  			out, _ = dockerCmd(c, "port", IDs[i])
    96  
    97  			err = assertPortList(c, out, []string{fmt.Sprintf("80/tcp -> 0.0.0.0:%d", 9090+i)})
    98  			// Port list is not correct
    99  			assert.NilError(c, err)
   100  		}
   101  
   102  		// test port range exhaustion
   103  		out, _, err = dockerCmdWithError("run", "-d",
   104  			"-p", "9090-9092:80",
   105  			"busybox", "top")
   106  		// Exhausted port range did not return an error
   107  		assert.Assert(c, err != nil, "out: %s", out)
   108  
   109  		for i := 0; i < 3; i++ {
   110  			dockerCmd(c, "rm", "-f", IDs[i])
   111  		}
   112  	}
   113  	testRange()
   114  	// Verify we ran re-use port ranges after they are no longer in use.
   115  	testRange()
   116  
   117  	// test invalid port ranges
   118  	for _, invalidRange := range []string{"9090-9089:80", "9090-:80", "-9090:80"} {
   119  		out, _, err = dockerCmdWithError("run", "-d",
   120  			"-p", invalidRange,
   121  			"busybox", "top")
   122  		// Port range should have returned an error
   123  		assert.Assert(c, err != nil, "out: %s", out)
   124  	}
   125  
   126  	// test host range:container range spec.
   127  	out, _ = dockerCmd(c, "run", "-d",
   128  		"-p", "9800-9803:80-83",
   129  		"busybox", "top")
   130  	ID = strings.TrimSpace(out)
   131  
   132  	out, _ = dockerCmd(c, "port", ID)
   133  
   134  	err = assertPortList(c, out, []string{
   135  		"80/tcp -> 0.0.0.0:9800",
   136  		"81/tcp -> 0.0.0.0:9801",
   137  		"82/tcp -> 0.0.0.0:9802",
   138  		"83/tcp -> 0.0.0.0:9803"})
   139  	// Port list is not correct
   140  	assert.NilError(c, err)
   141  	dockerCmd(c, "rm", "-f", ID)
   142  
   143  	// test mixing protocols in same port range
   144  	out, _ = dockerCmd(c, "run", "-d",
   145  		"-p", "8000-8080:80",
   146  		"-p", "8000-8080:80/udp",
   147  		"busybox", "top")
   148  	ID = strings.TrimSpace(out)
   149  
   150  	out, _ = dockerCmd(c, "port", ID)
   151  
   152  	// Running this test multiple times causes the TCP port to increment.
   153  	err = assertPortRange(c, out, []int{8000, 8080}, []int{8000, 8080})
   154  	// Port list is not correct
   155  	assert.NilError(c, err)
   156  	dockerCmd(c, "rm", "-f", ID)
   157  }
   158  
   159  func assertPortList(c *testing.T, out string, expected []string) error {
   160  	lines := strings.Split(strings.Trim(out, "\n "), "\n")
   161  	if len(lines) != len(expected) {
   162  		return fmt.Errorf("different size lists %s, %d, %d", out, len(lines), len(expected))
   163  	}
   164  	sort.Strings(lines)
   165  	sort.Strings(expected)
   166  
   167  	for i := 0; i < len(expected); i++ {
   168  		if lines[i] != expected[i] {
   169  			return fmt.Errorf("|" + lines[i] + "!=" + expected[i] + "|")
   170  		}
   171  	}
   172  
   173  	return nil
   174  }
   175  
   176  func assertPortRange(c *testing.T, out string, expectedTCP, expectedUDP []int) error {
   177  	lines := strings.Split(strings.Trim(out, "\n "), "\n")
   178  
   179  	var validTCP, validUDP bool
   180  	for _, l := range lines {
   181  		// 80/tcp -> 0.0.0.0:8015
   182  		port, err := strconv.Atoi(strings.Split(l, ":")[1])
   183  		if err != nil {
   184  			return err
   185  		}
   186  		if strings.Contains(l, "tcp") && expectedTCP != nil {
   187  			if port < expectedTCP[0] || port > expectedTCP[1] {
   188  				return fmt.Errorf("tcp port (%d) not in range expected range %d-%d", port, expectedTCP[0], expectedTCP[1])
   189  			}
   190  			validTCP = true
   191  		}
   192  		if strings.Contains(l, "udp") && expectedUDP != nil {
   193  			if port < expectedUDP[0] || port > expectedUDP[1] {
   194  				return fmt.Errorf("udp port (%d) not in range expected range %d-%d", port, expectedUDP[0], expectedUDP[1])
   195  			}
   196  			validUDP = true
   197  		}
   198  	}
   199  	if !validTCP {
   200  		return fmt.Errorf("tcp port not found")
   201  	}
   202  	if !validUDP {
   203  		return fmt.Errorf("udp port not found")
   204  	}
   205  	return nil
   206  }
   207  
   208  func stopRemoveContainer(id string, c *testing.T) {
   209  	dockerCmd(c, "rm", "-f", id)
   210  }
   211  
   212  func (s *DockerSuite) TestUnpublishedPortsInPsOutput(c *testing.T) {
   213  	testRequires(c, DaemonIsLinux)
   214  	// Run busybox with command line expose (equivalent to EXPOSE in image's Dockerfile) for the following ports
   215  	port1 := 80
   216  	port2 := 443
   217  	expose1 := fmt.Sprintf("--expose=%d", port1)
   218  	expose2 := fmt.Sprintf("--expose=%d", port2)
   219  	dockerCmd(c, "run", "-d", expose1, expose2, "busybox", "sleep", "5")
   220  
   221  	// Check docker ps o/p for last created container reports the unpublished ports
   222  	unpPort1 := fmt.Sprintf("%d/tcp", port1)
   223  	unpPort2 := fmt.Sprintf("%d/tcp", port2)
   224  	out, _ := dockerCmd(c, "ps", "-n=1")
   225  	// Missing unpublished ports in docker ps output
   226  	assert.Assert(c, strings.Contains(out, unpPort1))
   227  	// Missing unpublished ports in docker ps output
   228  	assert.Assert(c, strings.Contains(out, unpPort2))
   229  	// Run the container forcing to publish the exposed ports
   230  	dockerCmd(c, "run", "-d", "-P", expose1, expose2, "busybox", "sleep", "5")
   231  
   232  	// Check docker ps o/p for last created container reports the exposed ports in the port bindings
   233  	expBndRegx1 := regexp.MustCompile(`0.0.0.0:\d\d\d\d\d->` + unpPort1)
   234  	expBndRegx2 := regexp.MustCompile(`0.0.0.0:\d\d\d\d\d->` + unpPort2)
   235  	out, _ = dockerCmd(c, "ps", "-n=1")
   236  	// Cannot find expected port binding port (0.0.0.0:xxxxx->unpPort1) in docker ps output
   237  	assert.Equal(c, expBndRegx1.MatchString(out), true, fmt.Sprintf("out: %s; unpPort1: %s", out, unpPort1))
   238  	// Cannot find expected port binding port (0.0.0.0:xxxxx->unpPort2) in docker ps output
   239  	assert.Equal(c, expBndRegx2.MatchString(out), true, fmt.Sprintf("out: %s; unpPort2: %s", out, unpPort2))
   240  
   241  	// Run the container specifying explicit port bindings for the exposed ports
   242  	offset := 10000
   243  	pFlag1 := fmt.Sprintf("%d:%d", offset+port1, port1)
   244  	pFlag2 := fmt.Sprintf("%d:%d", offset+port2, port2)
   245  	out, _ = dockerCmd(c, "run", "-d", "-p", pFlag1, "-p", pFlag2, expose1, expose2, "busybox", "sleep", "5")
   246  	id := strings.TrimSpace(out)
   247  
   248  	// Check docker ps o/p for last created container reports the specified port mappings
   249  	expBnd1 := fmt.Sprintf("0.0.0.0:%d->%s", offset+port1, unpPort1)
   250  	expBnd2 := fmt.Sprintf("0.0.0.0:%d->%s", offset+port2, unpPort2)
   251  	out, _ = dockerCmd(c, "ps", "-n=1")
   252  	// Cannot find expected port binding (expBnd1) in docker ps output
   253  	assert.Assert(c, strings.Contains(out, expBnd1))
   254  	// Cannot find expected port binding (expBnd2) in docker ps output
   255  	assert.Assert(c, strings.Contains(out, expBnd2))
   256  	// Remove container now otherwise it will interfere with next test
   257  	stopRemoveContainer(id, c)
   258  
   259  	// Run the container with explicit port bindings and no exposed ports
   260  	out, _ = dockerCmd(c, "run", "-d", "-p", pFlag1, "-p", pFlag2, "busybox", "sleep", "5")
   261  	id = strings.TrimSpace(out)
   262  
   263  	// Check docker ps o/p for last created container reports the specified port mappings
   264  	out, _ = dockerCmd(c, "ps", "-n=1")
   265  	// Cannot find expected port binding (expBnd1) in docker ps output
   266  	assert.Assert(c, strings.Contains(out, expBnd1))
   267  	// Cannot find expected port binding (expBnd2) in docker ps output
   268  	assert.Assert(c, strings.Contains(out, expBnd2))
   269  	// Remove container now otherwise it will interfere with next test
   270  	stopRemoveContainer(id, c)
   271  
   272  	// Run the container with one unpublished exposed port and one explicit port binding
   273  	dockerCmd(c, "run", "-d", expose1, "-p", pFlag2, "busybox", "sleep", "5")
   274  
   275  	// Check docker ps o/p for last created container reports the specified unpublished port and port mapping
   276  	out, _ = dockerCmd(c, "ps", "-n=1")
   277  	// Missing unpublished exposed ports (unpPort1) in docker ps output
   278  	assert.Assert(c, strings.Contains(out, unpPort1))
   279  	// Missing port binding (expBnd2) in docker ps output
   280  	assert.Assert(c, strings.Contains(out, expBnd2))
   281  }
   282  
   283  func (s *DockerSuite) TestPortHostBinding(c *testing.T) {
   284  	testRequires(c, DaemonIsLinux, NotUserNamespace)
   285  	out, _ := dockerCmd(c, "run", "-d", "-p", "9876:80", "busybox",
   286  		"nc", "-l", "-p", "80")
   287  	firstID := strings.TrimSpace(out)
   288  
   289  	out, _ = dockerCmd(c, "port", firstID, "80")
   290  
   291  	err := assertPortList(c, out, []string{"0.0.0.0:9876"})
   292  	// Port list is not correct
   293  	assert.NilError(c, err)
   294  
   295  	dockerCmd(c, "run", "--net=host", "busybox",
   296  		"nc", "localhost", "9876")
   297  
   298  	dockerCmd(c, "rm", "-f", firstID)
   299  
   300  	out, _, err = dockerCmdWithError("run", "--net=host", "busybox", "nc", "localhost", "9876")
   301  	// Port is still bound after the Container is removed
   302  	assert.Assert(c, err != nil, "out: %s", out)
   303  }
   304  
   305  func (s *DockerSuite) TestPortExposeHostBinding(c *testing.T) {
   306  	testRequires(c, DaemonIsLinux, NotUserNamespace)
   307  	out, _ := dockerCmd(c, "run", "-d", "-P", "--expose", "80", "busybox",
   308  		"nc", "-l", "-p", "80")
   309  	firstID := strings.TrimSpace(out)
   310  
   311  	out, _ = dockerCmd(c, "port", firstID, "80")
   312  
   313  	_, exposedPort, err := net.SplitHostPort(out)
   314  	assert.Assert(c, err == nil, "out: %s", out)
   315  
   316  	dockerCmd(c, "run", "--net=host", "busybox",
   317  		"nc", "localhost", strings.TrimSpace(exposedPort))
   318  
   319  	dockerCmd(c, "rm", "-f", firstID)
   320  
   321  	out, _, err = dockerCmdWithError("run", "--net=host", "busybox",
   322  		"nc", "localhost", strings.TrimSpace(exposedPort))
   323  	// Port is still bound after the Container is removed
   324  	assert.Assert(c, err != nil, "out: %s", out)
   325  }
   326  
   327  func (s *DockerSuite) TestPortBindingOnSandbox(c *testing.T) {
   328  	testRequires(c, DaemonIsLinux, NotUserNamespace)
   329  	dockerCmd(c, "network", "create", "--internal", "-d", "bridge", "internal-net")
   330  	nr := getNetworkResource(c, "internal-net")
   331  	assert.Equal(c, nr.Internal, true)
   332  
   333  	dockerCmd(c, "run", "--net", "internal-net", "-d", "--name", "c1",
   334  		"-p", "8080:8080", "busybox", "nc", "-l", "-p", "8080")
   335  	assert.Assert(c, waitRun("c1") == nil)
   336  
   337  	_, _, err := dockerCmdWithError("run", "--net=host", "busybox", "nc", "localhost", "8080")
   338  	assert.Assert(c, err != nil, "Port mapping on internal network is expected to fail")
   339  	// Connect container to another normal bridge network
   340  	dockerCmd(c, "network", "create", "-d", "bridge", "foo-net")
   341  	dockerCmd(c, "network", "connect", "foo-net", "c1")
   342  
   343  	_, _, err = dockerCmdWithError("run", "--net=host", "busybox", "nc", "localhost", "8080")
   344  	assert.Assert(c, err == nil, "Port mapping on the new network is expected to succeed")
   345  }