Browse code

Merge pull request #9181 from icecrime/allocate_daemon_ports

Allocate daemon listening ports

Michael Crosby authored on 2014/12/18 11:01:20
Showing 2 changed files
... ...
@@ -27,6 +27,7 @@ import (
27 27
 
28 28
 	log "github.com/Sirupsen/logrus"
29 29
 	"github.com/docker/docker/api"
30
+	"github.com/docker/docker/daemon/networkdriver/portallocator"
30 31
 	"github.com/docker/docker/engine"
31 32
 	"github.com/docker/docker/pkg/listenbuffer"
32 33
 	"github.com/docker/docker/pkg/parsers"
... ...
@@ -1502,6 +1503,32 @@ func setupUnixHttp(addr string, job *engine.Job) (*HttpServer, error) {
1502 1502
 	return &HttpServer{&http.Server{Addr: addr, Handler: r}, l}, nil
1503 1503
 }
1504 1504
 
1505
+func allocateDaemonPort(addr string) error {
1506
+	host, port, err := net.SplitHostPort(addr)
1507
+	if err != nil {
1508
+		return err
1509
+	}
1510
+
1511
+	intPort, err := strconv.Atoi(port)
1512
+	if err != nil {
1513
+		return err
1514
+	}
1515
+
1516
+	var hostIPs []net.IP
1517
+	if parsedIP := net.ParseIP(host); parsedIP != nil {
1518
+		hostIPs = append(hostIPs, parsedIP)
1519
+	} else if hostIPs, err = net.LookupIP(host); err != nil {
1520
+		return fmt.Errorf("failed to lookup %s address in host specification", host)
1521
+	}
1522
+
1523
+	for _, hostIP := range hostIPs {
1524
+		if _, err := portallocator.RequestPort(hostIP, "tcp", intPort); err != nil {
1525
+			return fmt.Errorf("failed to allocate daemon listening port %d (err: %v)", intPort, err)
1526
+		}
1527
+	}
1528
+	return nil
1529
+}
1530
+
1505 1531
 func setupTcpHttp(addr string, job *engine.Job) (*HttpServer, error) {
1506 1532
 	if !strings.HasPrefix(addr, "127.0.0.1") && !job.GetenvBool("TlsVerify") {
1507 1533
 		log.Infof("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
... ...
@@ -1517,6 +1544,10 @@ func setupTcpHttp(addr string, job *engine.Job) (*HttpServer, error) {
1517 1517
 		return nil, err
1518 1518
 	}
1519 1519
 
1520
+	if err := allocateDaemonPort(addr); err != nil {
1521
+		return nil, err
1522
+	}
1523
+
1520 1524
 	if job.GetenvBool("Tls") || job.GetenvBool("TlsVerify") {
1521 1525
 		var tlsCa string
1522 1526
 		if job.GetenvBool("TlsVerify") {
... ...
@@ -2,6 +2,7 @@ package main
2 2
 
3 3
 import (
4 4
 	"encoding/json"
5
+	"fmt"
5 6
 	"io/ioutil"
6 7
 	"os"
7 8
 	"os/exec"
... ...
@@ -284,3 +285,33 @@ func TestDaemonLoggingLevel(t *testing.T) {
284 284
 
285 285
 	logDone("daemon - Logging Level")
286 286
 }
287
+
288
+func TestDaemonAllocatesListeningPort(t *testing.T) {
289
+	listeningPorts := [][]string{
290
+		{"0.0.0.0", "0.0.0.0", "5678"},
291
+		{"127.0.0.1", "127.0.0.1", "1234"},
292
+		{"localhost", "127.0.0.1", "1235"},
293
+	}
294
+
295
+	cmdArgs := []string{}
296
+	for _, hostDirective := range listeningPorts {
297
+		cmdArgs = append(cmdArgs, "--host", fmt.Sprintf("tcp://%s:%s", hostDirective[0], hostDirective[2]))
298
+	}
299
+
300
+	d := NewDaemon(t)
301
+	if err := d.StartWithBusybox(cmdArgs...); err != nil {
302
+		t.Fatalf("Could not start daemon with busybox: %v", err)
303
+	}
304
+	defer d.Stop()
305
+
306
+	for _, hostDirective := range listeningPorts {
307
+		output, err := d.Cmd("run", "-p", fmt.Sprintf("%s:%s:80", hostDirective[1], hostDirective[2]), "busybox", "true")
308
+		if err == nil {
309
+			t.Fatalf("Container should not start, expected port already allocated error: %q", output)
310
+		} else if !strings.Contains(output, "port is already allocated") {
311
+			t.Fatalf("Expected port is already allocated error: %q", output)
312
+		}
313
+	}
314
+
315
+	logDone("daemon - daemon listening port is allocated")
316
+}