From 34c29277c2c1fd1d1adc4409dc7075685f681de4 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Thu, 11 Feb 2016 13:30:23 -0500 Subject: [PATCH] Move listeners and port allocation outside the server. Signed-off-by: David Calavera --- api/server/server.go | 43 +++------ docker/daemon.go | 15 ++- docker/listeners/listeners.go | 22 +++++ .../listeners/listeners_unix.go | 93 ++++++++----------- .../listeners/listeners_windows.go | 36 +++---- 5 files changed, 97 insertions(+), 112 deletions(-) create mode 100644 docker/listeners/listeners.go rename api/server/server_unix.go => docker/listeners/listeners_unix.go (75%) rename api/server/server_windows.go => docker/listeners/listeners_windows.go (59%) diff --git a/api/server/server.go b/api/server/server.go index c15d268484..47ea51c268 100644 --- a/api/server/server.go +++ b/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 diff --git a/docker/daemon.go b/docker/daemon.go index 7289948692..9930dc277c 100644 --- a/docker/daemon.go +++ b/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 { diff --git a/docker/listeners/listeners.go b/docker/listeners/listeners.go new file mode 100644 index 0000000000..8150ba0c23 --- /dev/null +++ b/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 +} diff --git a/api/server/server_unix.go b/docker/listeners/listeners_unix.go similarity index 75% rename from api/server/server_unix.go rename to docker/listeners/listeners_unix.go index a4fc639575..1642ec4c6e 100644 --- a/api/server/server_unix.go +++ b/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 +} diff --git a/api/server/server_windows.go b/docker/listeners/listeners_windows.go similarity index 59% rename from api/server/server_windows.go rename to docker/listeners/listeners_windows.go index 613d185522..282b285256 100644 --- a/api/server/server_windows.go +++ b/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 }