Procházet zdrojové kódy

Allocate resources for server API before daemon creation

It prevents occupying of those resources (ports, unix-sockets) by
containers.
Also fixed false-positive test for that case.

Fix #15912

Signed-off-by: Alexander Morozov <lk4d4@docker.com>
Alexander Morozov před 9 roky
rodič
revize
5eda566f93

+ 35 - 35
api/server/server.go

@@ -2,7 +2,6 @@ package server
 
 import (
 	"crypto/tls"
-	"fmt"
 	"net"
 	"net/http"
 	"os"
@@ -28,22 +27,39 @@ type Config struct {
 	Version     string
 	SocketGroup string
 	TLSConfig   *tls.Config
+	Addrs       []Addr
 }
 
 // Server contains instance details for the server
 type Server struct {
 	cfg     *Config
 	start   chan struct{}
-	servers []serverCloser
+	servers []*HTTPServer
 	routers []router.Router
 }
 
+// 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.
-func New(cfg *Config) *Server {
-	return &Server{
+// It allocates resources which will be needed for ServeAPI(ports, unix-sockets).
+func New(cfg *Config) (*Server, error) {
+	s := &Server{
 		cfg:   cfg,
 		start: make(chan struct{}),
 	}
+	for _, addr := range cfg.Addrs {
+		srv, err := s.newServer(addr.Proto, addr.Addr)
+		if err != nil {
+			return nil, err
+		}
+		logrus.Debugf("Server created for HTTP on %s (%s)", addr.Proto, addr.Addr)
+		s.servers = append(s.servers, srv...)
+	}
+	return s, nil
 }
 
 // Close closes servers and thus stop receiving requests
@@ -55,39 +71,23 @@ func (s *Server) Close() {
 	}
 }
 
-type serverCloser interface {
-	Serve() error
-	Close() error
-}
-
-// ServeAPI loops through all of the protocols sent in to docker and spawns
-// off a go routine to setup a serving http.Server for each.
-func (s *Server) ServeAPI(protoAddrs []string) error {
-	var chErrors = make(chan error, len(protoAddrs))
-
-	for _, protoAddr := range protoAddrs {
-		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
-		if len(protoAddrParts) != 2 {
-			return fmt.Errorf("bad format, expected PROTO://ADDR")
-		}
-		srv, err := s.newServer(protoAddrParts[0], protoAddrParts[1])
-		if err != nil {
-			return err
-		}
-		s.servers = append(s.servers, srv...)
-
-		for _, s := range srv {
-			logrus.Infof("Listening for HTTP on %s (%s)", protoAddrParts[0], protoAddrParts[1])
-			go func(s serverCloser) {
-				if err := s.Serve(); err != nil && strings.Contains(err.Error(), "use of closed network connection") {
-					err = nil
-				}
-				chErrors <- err
-			}(s)
-		}
+// ServeAPI loops through all initialized servers and spawns goroutine
+// with Server method for each. It sets CreateMux() as Handler also.
+func (s *Server) ServeAPI() error {
+	var chErrors = make(chan error, len(s.servers))
+	for _, srv := range s.servers {
+		srv.srv.Handler = s.CreateMux()
+		go func(srv *HTTPServer) {
+			var err error
+			logrus.Errorf("API listen on %s", srv.l.Addr())
+			if err = srv.Serve(); err != nil && strings.Contains(err.Error(), "use of closed network connection") {
+				err = nil
+			}
+			chErrors <- err
+		}(srv)
 	}
 
-	for i := 0; i < len(protoAddrs); i++ {
+	for i := 0; i < len(s.servers); i++ {
 		err := <-chErrors
 		if err != nil {
 			return err

+ 5 - 5
api/server/server_unix.go

@@ -14,8 +14,9 @@ import (
 	systemdActivation "github.com/coreos/go-systemd/activation"
 )
 
-// newServer sets up the required serverClosers and does protocol specific checking.
-func (s *Server) newServer(proto, addr string) ([]serverCloser, error) {
+// 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
@@ -45,12 +46,11 @@ func (s *Server) newServer(proto, addr string) ([]serverCloser, error) {
 	default:
 		return nil, fmt.Errorf("Invalid protocol format: %q", proto)
 	}
-	var res []serverCloser
+	var res []*HTTPServer
 	for _, l := range ls {
 		res = append(res, &HTTPServer{
 			&http.Server{
-				Addr:    addr,
-				Handler: s.CreateMux(),
+				Addr: addr,
 			},
 			l,
 		})

+ 3 - 4
api/server/server_windows.go

@@ -9,7 +9,7 @@ import (
 )
 
 // NewServer sets up the required Server and does protocol specific checking.
-func (s *Server) newServer(proto, addr string) ([]serverCloser, error) {
+func (s *Server) newServer(proto, addr string) ([]*HTTPServer, error) {
 	var (
 		ls []net.Listener
 	)
@@ -25,12 +25,11 @@ func (s *Server) newServer(proto, addr string) ([]serverCloser, error) {
 		return nil, errors.New("Invalid protocol format. Windows only supports tcp.")
 	}
 
-	var res []serverCloser
+	var res []*HTTPServer
 	for _, l := range ls {
 		res = append(res, &HTTPServer{
 			&http.Server{
-				Addr:    addr,
-				Handler: s.CreateMux(),
+				Addr: addr,
 			},
 			l,
 		})

+ 12 - 2
docker/daemon.go

@@ -223,6 +223,17 @@ func (cli *DaemonCli) CmdDaemon(args ...string) error {
 		}
 		serverConfig.TLSConfig = tlsConfig
 	}
+	for _, protoAddr := range commonFlags.Hosts {
+		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
+		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]})
+	}
+	api, err := apiserver.New(serverConfig)
+	if err != nil {
+		logrus.Fatal(err)
+	}
 
 	if err := migrateKey(); err != nil {
 		logrus.Fatal(err)
@@ -249,7 +260,6 @@ func (cli *DaemonCli) CmdDaemon(args ...string) error {
 		"graphdriver": d.GraphDriver().String(),
 	}).Info("Docker daemon")
 
-	api := apiserver.New(serverConfig)
 	api.InitRouters(d)
 
 	// The serve API routine never exits unless an error occurs
@@ -257,7 +267,7 @@ func (cli *DaemonCli) CmdDaemon(args ...string) error {
 	// daemon doesn't exit
 	serveAPIWait := make(chan error)
 	go func() {
-		if err := api.ServeAPI(commonFlags.Hosts); err != nil {
+		if err := api.ServeAPI(); err != nil {
 			logrus.Errorf("ServeAPI error: %v", err)
 			serveAPIWait <- err
 			return

+ 1 - 1
integration-cli/docker_cli_daemon_test.go

@@ -1462,7 +1462,7 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithSocketAsVolume(c *check.C) {
 
 	socket := filepath.Join(s.d.folder, "docker.sock")
 
-	out, err := s.d.Cmd("run", "-d", "-v", socket+":/sock", "busybox")
+	out, err := s.d.Cmd("run", "-d", "--restart=always", "-v", socket+":/sock", "busybox")
 	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
 	c.Assert(s.d.Restart(), check.IsNil)
 }