From a1c8923e69105dddd1c0ef7e7a3cfff2866f40ac Mon Sep 17 00:00:00 2001 From: Drew Erny Date: Tue, 18 Oct 2016 11:09:47 -0700 Subject: [PATCH] support windows named pipe Adds support for windows named pipes by adding an xnet package containing functions for opening platform-agnostic local sockets Signed-off-by: Drew Erny --- manager/manager.go | 23 +++++++++++++++++------ node/node.go | 3 ++- xnet/xnet_unix.go | 20 ++++++++++++++++++++ xnet/xnet_windows.go | 22 ++++++++++++++++++++++ 4 files changed, 61 insertions(+), 7 deletions(-) create mode 100644 xnet/xnet_unix.go create mode 100644 xnet/xnet_windows.go diff --git a/manager/manager.go b/manager/manager.go index b73061b1a5..749d66bd78 100644 --- a/manager/manager.go +++ b/manager/manager.go @@ -7,6 +7,7 @@ import ( "net" "os" "path/filepath" + "runtime" "sync" "syscall" "time" @@ -30,6 +31,7 @@ import ( "github.com/docker/swarmkit/manager/state/raft" "github.com/docker/swarmkit/manager/state/store" "github.com/docker/swarmkit/protobuf/ptypes" + "github.com/docker/swarmkit/xnet" "github.com/pkg/errors" "golang.org/x/net/context" "google.golang.org/grpc" @@ -148,12 +150,15 @@ func New(config *Config) (*Manager, error) { tcpAddr = net.JoinHostPort("0.0.0.0", tcpAddrPort) } - err := os.MkdirAll(filepath.Dir(config.ProtoAddr["unix"]), 0700) - if err != nil { - return nil, errors.Wrap(err, "failed to create socket directory") + // don't create a socket directory if we're on windows. we used named pipe + if runtime.GOOS != "windows" { + err := os.MkdirAll(filepath.Dir(config.ProtoAddr["unix"]), 0700) + if err != nil { + return nil, errors.Wrap(err, "failed to create socket directory") + } } - err = os.MkdirAll(config.StateDir, 0700) + err := os.MkdirAll(config.StateDir, 0700) if err != nil { return nil, errors.Wrap(err, "failed to create state directory") } @@ -171,7 +176,13 @@ func New(config *Config) (*Manager, error) { listeners = make(map[string]net.Listener) for proto, addr := range config.ProtoAddr { - l, err := net.Listen(proto, addr) + var l net.Listener + var err error + if proto == "unix" { + l, err = xnet.ListenLocal(addr) + } else { + l, err = net.Listen(proto, addr) + } // A unix socket may fail to bind if the file already // exists. Try replacing the file. @@ -184,7 +195,7 @@ func New(config *Config) (*Manager, error) { } if proto == "unix" && unwrappedErr == syscall.EADDRINUSE { os.Remove(addr) - l, err = net.Listen(proto, addr) + l, err = xnet.ListenLocal(addr) if err != nil { return nil, err } diff --git a/node/node.go b/node/node.go index d673b6a5c7..c2b52e4579 100644 --- a/node/node.go +++ b/node/node.go @@ -22,6 +22,7 @@ import ( "github.com/docker/swarmkit/log" "github.com/docker/swarmkit/manager" "github.com/docker/swarmkit/remotes" + "github.com/docker/swarmkit/xnet" "github.com/pkg/errors" "golang.org/x/net/context" "google.golang.org/grpc" @@ -559,7 +560,7 @@ func (n *Node) initManagerConnection(ctx context.Context, ready chan<- struct{}) addr := n.config.ListenControlAPI opts = append(opts, grpc.WithDialer( func(addr string, timeout time.Duration) (net.Conn, error) { - return net.DialTimeout("unix", addr, timeout) + return xnet.DialTimeoutLocal(addr, timeout) })) conn, err := grpc.Dial(addr, opts...) if err != nil { diff --git a/xnet/xnet_unix.go b/xnet/xnet_unix.go new file mode 100644 index 0000000000..7dc7732345 --- /dev/null +++ b/xnet/xnet_unix.go @@ -0,0 +1,20 @@ +// +build !windows + +package xnet + +import ( + "net" + "time" +) + +// ListenLocal opens a local socket for control communication +func ListenLocal(socket string) (net.Listener, error) { + // on unix it's just a unix socket + return net.Listen("unix", socket) +} + +// DialTimeoutLocal is a DialTimeout function for local sockets +func DialTimeoutLocal(socket string, timeout time.Duration) (net.Conn, error) { + // on unix, we dial a unix socket + return net.DialTimeout("unix", socket, timeout) +} diff --git a/xnet/xnet_windows.go b/xnet/xnet_windows.go new file mode 100644 index 0000000000..3f97514446 --- /dev/null +++ b/xnet/xnet_windows.go @@ -0,0 +1,22 @@ +// +build windows + +package xnet + +import ( + "net" + "time" + + "github.com/Microsoft/go-winio" +) + +// ListenLocal opens a local socket for control communication +func ListenLocal(socket string) (net.Listener, error) { + // on windows, our socket is actually a named pipe + return winio.ListenPipe(socket, nil) +} + +// DialTimeoutLocal is a DialTimeout function for local sockets +func DialTimeoutLocal(socket string, timeout time.Duration) (net.Conn, error) { + // On windows, we dial a named pipe + return winio.DialPipe(socket, &timeout) +}