Browse Source

Merge pull request #20239 from calavera/remove_server_port_allocation

Move listeners and port allocation outside the server.
Alexander Morozov 9 years ago
parent
commit
02a37a281c

+ 13 - 30
api/server/server.go

@@ -11,7 +11,6 @@ import (
 	"github.com/docker/docker/api/server/router"
 	"github.com/docker/docker/pkg/authorization"
 	"github.com/docker/docker/utils"
-	"github.com/docker/go-connections/sockets"
 	"github.com/gorilla/mux"
 	"golang.org/x/net/context"
 )
@@ -29,7 +28,6 @@ type Config struct {
 	Version                  string
 	SocketGroup              string
 	TLSConfig                *tls.Config
-	Addrs                    []Addr
 }
 
 // Server contains instance details for the server
@@ -41,27 +39,25 @@ type Server struct {
 	routerSwapper *routerSwapper
 }
 
-// Addr contains string representation of address and its protocol (tcp, unix...).
-type Addr struct {
-	Proto string
-	Addr  string
-}
-
 // New returns a new instance of the server based on the specified configuration.
 // It allocates resources which will be needed for ServeAPI(ports, unix-sockets).
-func New(cfg *Config) (*Server, error) {
-	s := &Server{
+func New(cfg *Config) *Server {
+	return &Server{
 		cfg: cfg,
 	}
-	for _, addr := range cfg.Addrs {
-		srv, err := s.newServer(addr.Proto, addr.Addr)
-		if err != nil {
-			return nil, err
+}
+
+// Accept sets a listener the server accepts connections into.
+func (s *Server) Accept(addr string, listeners ...net.Listener) {
+	for _, listener := range listeners {
+		httpServer := &HTTPServer{
+			srv: &http.Server{
+				Addr: addr,
+			},
+			l: listener,
 		}
-		logrus.Debugf("Server created for HTTP on %s (%s)", addr.Proto, addr.Addr)
-		s.servers = append(s.servers, srv...)
+		s.servers = append(s.servers, httpServer)
 	}
-	return s, nil
 }
 
 // Close closes servers and thus stop receiving requests
@@ -126,19 +122,6 @@ func writeCorsHeaders(w http.ResponseWriter, r *http.Request, corsHeaders string
 	w.Header().Add("Access-Control-Allow-Methods", "HEAD, GET, POST, DELETE, PUT, OPTIONS")
 }
 
-func (s *Server) initTCPSocket(addr string) (l net.Listener, err error) {
-	if s.cfg.TLSConfig == nil || s.cfg.TLSConfig.ClientAuth != tls.RequireAndVerifyClientCert {
-		logrus.Warn("/!\\ DON'T BIND ON ANY IP ADDRESS WITHOUT setting -tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
-	}
-	if l, err = sockets.NewTCPSocket(addr, s.cfg.TLSConfig); err != nil {
-		return nil, err
-	}
-	if err := allocateDaemonPort(addr); err != nil {
-		return nil, err
-	}
-	return
-}
-
 func (s *Server) makeHTTPHandler(handler httputils.APIFunc) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
 		// log the handler call

+ 10 - 5
docker/daemon.go

@@ -25,6 +25,7 @@ import (
 	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/daemon"
 	"github.com/docker/docker/daemon/logger"
+	"github.com/docker/docker/docker/listeners"
 	"github.com/docker/docker/dockerversion"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/pkg/jsonlog"
@@ -233,6 +234,9 @@ func (cli *DaemonCli) CmdDaemon(args ...string) error {
 	if len(cli.Config.Hosts) == 0 {
 		cli.Config.Hosts = make([]string, 1)
 	}
+
+	api := apiserver.New(serverConfig)
+
 	for i := 0; i < len(cli.Config.Hosts); i++ {
 		var err error
 		if cli.Config.Hosts[i], err = opts.ParseHost(cli.Config.TLS, cli.Config.Hosts[i]); err != nil {
@@ -244,12 +248,13 @@ func (cli *DaemonCli) CmdDaemon(args ...string) error {
 		if len(protoAddrParts) != 2 {
 			logrus.Fatalf("bad format %s, expected PROTO://ADDR", protoAddr)
 		}
-		serverConfig.Addrs = append(serverConfig.Addrs, apiserver.Addr{Proto: protoAddrParts[0], Addr: protoAddrParts[1]})
-	}
+		l, err := listeners.Init(protoAddrParts[0], protoAddrParts[1], serverConfig.SocketGroup, serverConfig.TLSConfig)
+		if err != nil {
+			logrus.Fatal(err)
+		}
 
-	api, err := apiserver.New(serverConfig)
-	if err != nil {
-		logrus.Fatal(err)
+		logrus.Debugf("Listener created for HTTP on %s (%s)", protoAddrParts[0], protoAddrParts[1])
+		api.Accept(protoAddrParts[1], l...)
 	}
 
 	if err := migrateKey(); err != nil {

+ 22 - 0
docker/listeners/listeners.go

@@ -0,0 +1,22 @@
+package listeners
+
+import (
+	"crypto/tls"
+	"net"
+
+	"github.com/Sirupsen/logrus"
+	"github.com/docker/go-connections/sockets"
+)
+
+func initTCPSocket(addr string, tlsConfig *tls.Config) (l net.Listener, err error) {
+	if tlsConfig == nil || tlsConfig.ClientAuth != tls.RequireAndVerifyClientCert {
+		logrus.Warn("/!\\ DON'T BIND ON ANY IP ADDRESS WITHOUT setting -tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
+	}
+	if l, err = sockets.NewTCPSocket(addr, tlsConfig); err != nil {
+		return nil, err
+	}
+	if err := allocateDaemonPort(addr); err != nil {
+		return nil, err
+	}
+	return
+}

+ 40 - 53
api/server/server_unix.go → docker/listeners/listeners_unix.go

@@ -1,42 +1,35 @@
-// +build freebsd linux
+// +build !windows
 
-package server
+package listeners
 
 import (
 	"crypto/tls"
 	"fmt"
 	"net"
-	"net/http"
 	"strconv"
 
 	"github.com/Sirupsen/logrus"
+	"github.com/coreos/go-systemd/activation"
 	"github.com/docker/go-connections/sockets"
 	"github.com/docker/libnetwork/portallocator"
-
-	systemdActivation "github.com/coreos/go-systemd/activation"
 )
 
-// newServer sets up the required HTTPServers and does protocol specific checking.
-// newServer does not set any muxers, you should set it later to Handler field
-func (s *Server) newServer(proto, addr string) ([]*HTTPServer, error) {
-	var (
-		err error
-		ls  []net.Listener
-	)
+// Init creates new listeners for the server.
+func Init(proto, addr, socketGroup string, tlsConfig *tls.Config) (ls []net.Listener, err error) {
 	switch proto {
 	case "fd":
-		ls, err = listenFD(addr, s.cfg.TLSConfig)
+		ls, err = listenFD(addr, tlsConfig)
 		if err != nil {
 			return nil, err
 		}
 	case "tcp":
-		l, err := s.initTCPSocket(addr)
+		l, err := initTCPSocket(addr, tlsConfig)
 		if err != nil {
 			return nil, err
 		}
 		ls = append(ls, l)
 	case "unix":
-		l, err := sockets.NewUnixSocket(addr, s.cfg.SocketGroup)
+		l, err := sockets.NewUnixSocket(addr, socketGroup)
 		if err != nil {
 			return nil, fmt.Errorf("can't create unix socket %s: %v", addr, err)
 		}
@@ -44,43 +37,8 @@ func (s *Server) newServer(proto, addr string) ([]*HTTPServer, error) {
 	default:
 		return nil, fmt.Errorf("Invalid protocol format: %q", proto)
 	}
-	var res []*HTTPServer
-	for _, l := range ls {
-		res = append(res, &HTTPServer{
-			&http.Server{
-				Addr: addr,
-			},
-			l,
-		})
-	}
-	return res, nil
-}
 
-func allocateDaemonPort(addr string) error {
-	host, port, err := net.SplitHostPort(addr)
-	if err != nil {
-		return err
-	}
-
-	intPort, err := strconv.Atoi(port)
-	if err != nil {
-		return err
-	}
-
-	var hostIPs []net.IP
-	if parsedIP := net.ParseIP(host); parsedIP != nil {
-		hostIPs = append(hostIPs, parsedIP)
-	} else if hostIPs, err = net.LookupIP(host); err != nil {
-		return fmt.Errorf("failed to lookup %s address in host specification", host)
-	}
-
-	pa := portallocator.Get()
-	for _, hostIP := range hostIPs {
-		if _, err := pa.RequestPort(hostIP, "tcp", intPort); err != nil {
-			return fmt.Errorf("failed to allocate daemon listening port %d (err: %v)", intPort, err)
-		}
-	}
-	return nil
+	return
 }
 
 // listenFD returns the specified socket activated files as a slice of
@@ -92,9 +50,9 @@ func listenFD(addr string, tlsConfig *tls.Config) ([]net.Listener, error) {
 	)
 	// socket activation
 	if tlsConfig != nil {
-		listeners, err = systemdActivation.TLSListeners(false, tlsConfig)
+		listeners, err = activation.TLSListeners(false, tlsConfig)
 	} else {
-		listeners, err = systemdActivation.Listeners(false)
+		listeners, err = activation.Listeners(false)
 	}
 	if err != nil {
 		return nil, err
@@ -130,3 +88,32 @@ func listenFD(addr string, tlsConfig *tls.Config) ([]net.Listener, error) {
 	}
 	return []net.Listener{listeners[fdOffset]}, nil
 }
+
+// allocateDaemonPort ensures that there are no containers
+// that try to use any port allocated for the docker server.
+func allocateDaemonPort(addr string) error {
+	host, port, err := net.SplitHostPort(addr)
+	if err != nil {
+		return err
+	}
+
+	intPort, err := strconv.Atoi(port)
+	if err != nil {
+		return err
+	}
+
+	var hostIPs []net.IP
+	if parsedIP := net.ParseIP(host); parsedIP != nil {
+		hostIPs = append(hostIPs, parsedIP)
+	} else if hostIPs, err = net.LookupIP(host); err != nil {
+		return fmt.Errorf("failed to lookup %s address in host specification", host)
+	}
+
+	pa := portallocator.Get()
+	for _, hostIP := range hostIPs {
+		if _, err := pa.RequestPort(hostIP, "tcp", intPort); err != nil {
+			return fmt.Errorf("failed to allocate daemon listening port %d (err: %v)", intPort, err)
+		}
+	}
+	return nil
+}

+ 12 - 24
api/server/server_windows.go → docker/listeners/listeners_windows.go

@@ -1,24 +1,20 @@
-// +build windows
-
-package server
+package listeners
 
 import (
+	"crypto/tls"
 	"errors"
 	"fmt"
-	"github.com/Microsoft/go-winio"
 	"net"
-	"net/http"
 	"strings"
+
+	"github.com/Microsoft/go-winio"
 )
 
-// NewServer sets up the required Server and does protocol specific checking.
-func (s *Server) newServer(proto, addr string) ([]*HTTPServer, error) {
-	var (
-		ls []net.Listener
-	)
+// Init creates new listeners for the server.
+func Init(proto, addr, socketGroup string, tlsConfig *tls.Config) (ls []net.Listener, err error) {
 	switch proto {
 	case "tcp":
-		l, err := s.initTCPSocket(addr)
+		l, err := initTCPSocket(addr, tlsConfig)
 		if err != nil {
 			return nil, err
 		}
@@ -27,8 +23,8 @@ func (s *Server) newServer(proto, addr string) ([]*HTTPServer, error) {
 	case "npipe":
 		// allow Administrators and SYSTEM, plus whatever additional users or groups were specified
 		sddl := "D:P(A;;GA;;;BA)(A;;GA;;;SY)"
-		if s.cfg.SocketGroup != "" {
-			for _, g := range strings.Split(s.cfg.SocketGroup, ",") {
+		if socketGroup != "" {
+			for _, g := range strings.Split(socketGroup, ",") {
 				sid, err := winio.LookupSidByName(g)
 				if err != nil {
 					return nil, err
@@ -46,19 +42,11 @@ func (s *Server) newServer(proto, addr string) ([]*HTTPServer, error) {
 		return nil, errors.New("Invalid protocol format. Windows only supports tcp and npipe.")
 	}
 
-	var res []*HTTPServer
-	for _, l := range ls {
-		res = append(res, &HTTPServer{
-			&http.Server{
-				Addr: addr,
-			},
-			l,
-		})
-	}
-	return res, nil
-
+	return
 }
 
+// allocateDaemonPort ensures that there are no containers
+// that try to use any port allocated for the docker server.
 func allocateDaemonPort(addr string) error {
 	return nil
 }