virtual-vxlan/grpc_windows.go

127 lines
3.2 KiB
Go

package main
import (
"context"
"fmt"
"net"
"os"
"strings"
"time"
"github.com/Microsoft/go-winio"
pb "github.com/grmrgecko/virtual-vxlan/vxlan"
log "github.com/sirupsen/logrus"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
// Allows go generate to compile the protobuf to golang.
//go:generate protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative ./vxlan/vxlan.proto
// GRPC server structure.
type GRPCServer struct {
pb.UnimplementedVxlanServer
RPCPath string
server *grpc.Server
}
// Start serving GRPC requests.
func (s *GRPCServer) Serve(li net.Listener) {
err := s.server.Serve(li)
if err != nil {
log.Errorf("Error serving grpc: %v", err)
}
}
// Stop GRPC server.
func (s *GRPCServer) Close() {
s.server.Stop()
}
// Dialer for named pipes to allow connecting GRPC via named pipes.
func pipeDialer(ctx context.Context, addr string) (net.Conn, error) {
return winio.DialPipe(addr, nil)
}
// Verifies the RPC UNIX path is still listening if it exists.
func RPCCleanPath(rpcPath string) {
if !strings.HasPrefix(rpcPath, `\\.\`) {
// Check if the RPC socket already exists.
_, err := os.Stat(rpcPath)
if err == nil {
// If the socket exists, see if its listening.
l, err := net.Dial("unix", rpcPath)
// If its not listening, remove it to allow us to start.
if err != nil {
os.Remove(rpcPath)
} else {
l.Close()
}
}
}
}
// Start GRPC server.
func NewGRPCServer(rpcPath string) (s *GRPCServer, err error) {
// Verify another server doesn't exist.
if app.grpcServer != nil {
return nil, fmt.Errorf("grpc server is already running")
}
// Connect to RPC path.
var li net.Listener
if strings.HasPrefix(rpcPath, `\\.\`) {
li, err = winio.ListenPipe(rpcPath, nil)
} else {
li, err = net.Listen("unix", rpcPath)
}
if err != nil {
return nil, fmt.Errorf("failed to listen on socket: %v", err)
}
// Setup server.
s = new(GRPCServer)
s.server = grpc.NewServer()
// Register the vxlan service to this server.
pb.RegisterVxlanServer(s.server, s)
// Update the global app gRPC server.
app.grpcServer = s
// Start serving requests.
go s.Serve(li)
return s, nil
}
// Start a connection to the gRPC Server.
func NewGRPCClient() (c pb.VxlanClient, conn *grpc.ClientConn, err error) {
// Read the minimal config.
config := ReadMinimalConfig()
// Start an gRPC client connection to the unix socket.
if strings.HasPrefix(config.RPCPath, `\\.\`) {
// Attempt to connect using named pipes.
dialOption := grpc.WithContextDialer(pipeDialer)
conn, err = grpc.DialContext(
context.Background(),
config.RPCPath,
dialOption,
grpc.WithTransportCredentials(insecure.NewCredentials()),
// Add a timeout for the initial connection
grpc.WithBlock(),
grpc.WithTimeout(5*time.Second),
)
} else {
conn, err = grpc.NewClient(fmt.Sprintf("unix:%s", config.RPCPath), grpc.WithTransportCredentials(insecure.NewCredentials()))
}
// If connection is successful, provide client to the vxlan service.
if err == nil {
c = pb.NewVxlanClient(conn)
}
return
}