feat: add vsock network type support (#1303)

So we support connecting dfdaemon running on host outside of VM via
virtio-vsock device.

Signed-off-by: Eryu Guan <eguan@linux.alibaba.com>
This commit is contained in:
Eryu Guan 2022-05-16 14:34:27 +08:00 committed by GitHub
parent 8f26e3213c
commit 0bc0d3b7ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 102 additions and 5 deletions

2
go.mod
View File

@ -41,6 +41,7 @@ require (
github.com/jarcoal/httpmock v1.0.8
github.com/looplab/fsm v0.3.0
github.com/mcuadros/go-gin-prometheus v0.1.0
github.com/mdlayher/vsock v1.1.1
github.com/mitchellh/mapstructure v1.4.1
github.com/montanaflynn/stats v0.6.6
github.com/onsi/ginkgo/v2 v2.1.0
@ -155,6 +156,7 @@ require (
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/mdlayher/socket v0.2.0 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect

4
go.sum
View File

@ -642,6 +642,10 @@ github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182aff
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/mcuadros/go-gin-prometheus v0.1.0 h1:JNoWKvw/u9tyRJ8BL9ZJvfiXU8IHUw8gCvcf/5L8tnI=
github.com/mcuadros/go-gin-prometheus v0.1.0/go.mod h1:ezECAsiHtCRIa+6Ii8THg7G7RJvpO4S19d499UkEE3s=
github.com/mdlayher/socket v0.2.0 h1:EY4YQd6hTAg2tcXF84p5DTHazShE50u5HeBzBaNgjkA=
github.com/mdlayher/socket v0.2.0/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL1CV+f0E=
github.com/mdlayher/vsock v1.1.1 h1:8lFuiXQnmICBrCIIA9PMgVSke6Fg6V4+r0v7r55k88I=
github.com/mdlayher/vsock v1.1.1/go.mod h1:Y43jzcy7KM3QB+/FK15pfqGxDMCMzUXWegEfIbSM18U=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=

View File

@ -26,8 +26,13 @@ import (
type NetworkType string
const (
TCP NetworkType = "tcp"
UNIX NetworkType = "unix"
TCP NetworkType = "tcp"
UNIX NetworkType = "unix"
VSOCK NetworkType = "vsock"
TCPEndpointPrefix string = "dns:///"
UnixEndpointPrefix string = "unix://"
VsockEndpointPrefix string = "vsock://"
)
type NetAddr struct {
@ -39,9 +44,11 @@ type NetAddr struct {
func (n NetAddr) GetEndpoint() string {
switch n.Type {
case UNIX:
return "unix://" + n.Addr
return UnixEndpointPrefix + n.Addr
case VSOCK:
return VsockEndpointPrefix + n.Addr
default:
return "dns:///" + n.Addr
return TCPEndpointPrefix + n.Addr
}
}

View File

@ -41,10 +41,15 @@ func GetClientByAddr(addrs []dfnet.NetAddr, opts ...grpc.DialOption) (DaemonClie
if len(addrs) == 0 {
return nil, errors.New("address list of daemon is empty")
}
dialOpts, err := rpc.VsockDialerOption(addrs, opts)
if err != nil {
return nil, err
}
dc := &daemonClient{
rpc.NewConnection(context.Background(), "daemon-static", addrs, []rpc.ConnOption{
rpc.WithConnExpireTime(60 * time.Second),
rpc.WithDialOption(opts),
rpc.WithDialOption(dialOpts),
}),
}
return dc, nil

79
pkg/rpc/vsock.go Normal file
View File

@ -0,0 +1,79 @@
/*
* Copyright 2022 The Dragonfly Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rpc
import (
"context"
"fmt"
"net"
"strconv"
"strings"
"github.com/mdlayher/vsock"
"github.com/pkg/errors"
"google.golang.org/grpc"
"d7y.io/dragonfly/v2/pkg/dfnet"
)
// VsockDialer is the dialer for vsock, it expects `address` to be in dfnet.NetAddr.GetEndpoint()
// format, that is "vsock://cid:port"
func VsockDialer(_ctx context.Context, address string) (net.Conn, error) {
addrStr := strings.TrimPrefix(address, dfnet.VsockEndpointPrefix)
addr := strings.Split(addrStr, ":")
if len(addr) != 2 {
return nil, fmt.Errorf("invalid vsock address (%s), expected %scid:port", address, dfnet.VsockEndpointPrefix)
}
cid, err := strconv.ParseUint(addr[0], 10, 32)
if err != nil {
return nil, errors.Wrapf(err, "failed to convert %q to vsock cid", addr[0])
}
port, err := strconv.ParseUint(addr[1], 10, 32)
if err != nil {
return nil, errors.Wrapf(err, "failed to convert %q to vsock port", addr[1])
}
conn, err := vsock.Dial(uint32(cid), uint32(port), nil)
if err != nil {
return nil, errors.Wrapf(err, "failed to dial vsock %v:%v, address %s", uint32(cid), uint32(port), address)
}
return conn, nil
}
// If `addrs` are all vsock addresses, add rpc.VsockDialer to DialOption, and return error if addrs
// have mixed vsock and other connection types.
func VsockDialerOption(addrs []dfnet.NetAddr, opts []grpc.DialOption) ([]grpc.DialOption, error) {
var prevType dfnet.NetworkType
hasVsock := false
for n, a := range addrs {
if a.Type != dfnet.VSOCK {
prevType = a.Type
continue
}
hasVsock = true
if n > 0 && prevType != dfnet.VSOCK {
return nil, fmt.Errorf("addrs(%v) have mixed vsock and other types", addrs)
}
prevType = a.Type
}
dialOpts := opts
if hasVsock {
dialOpts = append(opts, grpc.WithContextDialer(VsockDialer))
}
return dialOpts, nil
}