Move listeners and port allocation outside the server.

Signed-off-by: David Calavera <david.calavera@gmail.com>
Upstream-commit: 34c29277c2c1fd1d1adc4409dc7075685f681de4
Component: engine
This commit is contained in:
David Calavera
2016-02-11 13:30:23 -05:00
parent 3f33957369
commit c9c9d43ade
5 changed files with 97 additions and 112 deletions

View File

@ -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

View File

@ -1,132 +0,0 @@
// +build freebsd linux
package server
import (
"crypto/tls"
"fmt"
"net"
"net/http"
"strconv"
"github.com/Sirupsen/logrus"
"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
)
switch proto {
case "fd":
ls, err = listenFD(addr, s.cfg.TLSConfig)
if err != nil {
return nil, err
}
case "tcp":
l, err := s.initTCPSocket(addr)
if err != nil {
return nil, err
}
ls = append(ls, l)
case "unix":
l, err := sockets.NewUnixSocket(addr, s.cfg.SocketGroup)
if err != nil {
return nil, fmt.Errorf("can't create unix socket %s: %v", addr, err)
}
ls = append(ls, l)
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
}
// listenFD returns the specified socket activated files as a slice of
// net.Listeners or all of the activated files if "*" is given.
func listenFD(addr string, tlsConfig *tls.Config) ([]net.Listener, error) {
var (
err error
listeners []net.Listener
)
// socket activation
if tlsConfig != nil {
listeners, err = systemdActivation.TLSListeners(false, tlsConfig)
} else {
listeners, err = systemdActivation.Listeners(false)
}
if err != nil {
return nil, err
}
if len(listeners) == 0 {
return nil, fmt.Errorf("No sockets found")
}
// default to all fds just like unix:// and tcp://
if addr == "" || addr == "*" {
return listeners, nil
}
fdNum, err := strconv.Atoi(addr)
if err != nil {
return nil, fmt.Errorf("failed to parse systemd address, should be number: %v", err)
}
fdOffset := fdNum - 3
if len(listeners) < int(fdOffset)+1 {
return nil, fmt.Errorf("Too few socket activated files passed in")
}
if listeners[fdOffset] == nil {
return nil, fmt.Errorf("failed to listen on systemd activated file at fd %d", fdOffset+3)
}
for i, ls := range listeners {
if i == fdOffset || ls == nil {
continue
}
if err := ls.Close(); err != nil {
logrus.Errorf("Failed to close systemd activated file at fd %d: %v", fdOffset+3, err)
}
}
return []net.Listener{listeners[fdOffset]}, nil
}

View File

@ -1,64 +0,0 @@
// +build windows
package server
import (
"errors"
"fmt"
"github.com/Microsoft/go-winio"
"net"
"net/http"
"strings"
)
// NewServer sets up the required Server and does protocol specific checking.
func (s *Server) newServer(proto, addr string) ([]*HTTPServer, error) {
var (
ls []net.Listener
)
switch proto {
case "tcp":
l, err := s.initTCPSocket(addr)
if err != nil {
return nil, err
}
ls = append(ls, l)
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, ",") {
sid, err := winio.LookupSidByName(g)
if err != nil {
return nil, err
}
sddl += fmt.Sprintf("(A;;GRGW;;;%s)", sid)
}
}
l, err := winio.ListenPipe(addr, sddl)
if err != nil {
return nil, err
}
ls = append(ls, l)
default:
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
}
func allocateDaemonPort(addr string) error {
return nil
}