mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
171 lines
4.0 KiB
Go
171 lines
4.0 KiB
Go
// Copyright 2018 The etcd 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 agent
|
|
|
|
import (
|
|
"math"
|
|
"net"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
|
|
"go.etcd.io/etcd/pkg/v3/proxy"
|
|
"go.etcd.io/etcd/server/v3/embed"
|
|
"go.etcd.io/etcd/tests/v3/functional/rpcpb"
|
|
|
|
"go.uber.org/zap"
|
|
"google.golang.org/grpc"
|
|
)
|
|
|
|
// Server implements "rpcpb.TransportServer"
|
|
// and other etcd operations as an agent
|
|
// no need to lock fields since request operations are
|
|
// serialized in tester-side
|
|
type Server struct {
|
|
lg *zap.Logger
|
|
|
|
grpcServer *grpc.Server
|
|
|
|
network string
|
|
address string
|
|
ln net.Listener
|
|
|
|
rpcpb.TransportServer
|
|
last rpcpb.Operation
|
|
|
|
*rpcpb.Member
|
|
*rpcpb.Tester
|
|
|
|
etcdServer *embed.Etcd
|
|
etcdCmd *exec.Cmd
|
|
etcdLogFile *os.File
|
|
|
|
// forward incoming advertise URLs traffic to listen URLs
|
|
advertiseClientPortToProxy map[int]proxy.Server
|
|
advertisePeerPortToProxy map[int]proxy.Server
|
|
}
|
|
|
|
// NewServer returns a new agent server.
|
|
func NewServer(
|
|
lg *zap.Logger,
|
|
network string,
|
|
address string,
|
|
) *Server {
|
|
return &Server{
|
|
lg: lg,
|
|
network: network,
|
|
address: address,
|
|
last: rpcpb.Operation_NOT_STARTED,
|
|
advertiseClientPortToProxy: make(map[int]proxy.Server),
|
|
advertisePeerPortToProxy: make(map[int]proxy.Server),
|
|
}
|
|
}
|
|
|
|
const (
|
|
maxRequestBytes = 1.5 * 1024 * 1024
|
|
grpcOverheadBytes = 512 * 1024
|
|
maxStreams = math.MaxUint32
|
|
maxSendBytes = math.MaxInt32
|
|
)
|
|
|
|
// StartServe starts serving agent server.
|
|
func (srv *Server) StartServe() error {
|
|
var err error
|
|
srv.ln, err = net.Listen(srv.network, srv.address)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var opts []grpc.ServerOption
|
|
opts = append(opts, grpc.MaxRecvMsgSize(int(maxRequestBytes+grpcOverheadBytes)))
|
|
opts = append(opts, grpc.MaxSendMsgSize(maxSendBytes))
|
|
opts = append(opts, grpc.MaxConcurrentStreams(maxStreams))
|
|
srv.grpcServer = grpc.NewServer(opts...)
|
|
|
|
rpcpb.RegisterTransportServer(srv.grpcServer, srv)
|
|
|
|
srv.lg.Info(
|
|
"gRPC server started",
|
|
zap.String("address", srv.address),
|
|
zap.String("listener-address", srv.ln.Addr().String()),
|
|
)
|
|
err = srv.grpcServer.Serve(srv.ln)
|
|
if err != nil && strings.Contains(err.Error(), "use of closed network connection") {
|
|
srv.lg.Info(
|
|
"gRPC server is shut down",
|
|
zap.String("address", srv.address),
|
|
zap.Error(err),
|
|
)
|
|
} else {
|
|
srv.lg.Warn(
|
|
"gRPC server returned with error",
|
|
zap.String("address", srv.address),
|
|
zap.Error(err),
|
|
)
|
|
}
|
|
return err
|
|
}
|
|
|
|
// Stop stops serving gRPC server.
|
|
func (srv *Server) Stop() {
|
|
srv.lg.Info("gRPC server stopping", zap.String("address", srv.address))
|
|
srv.grpcServer.Stop()
|
|
srv.lg.Info("gRPC server stopped", zap.String("address", srv.address))
|
|
}
|
|
|
|
// Transport communicates with etcd tester.
|
|
func (srv *Server) Transport(stream rpcpb.Transport_TransportServer) (reterr error) {
|
|
errc := make(chan error, 1)
|
|
go func() {
|
|
for {
|
|
var req *rpcpb.Request
|
|
var err error
|
|
req, err = stream.Recv()
|
|
if err != nil {
|
|
errc <- err
|
|
// TODO: handle error and retry
|
|
return
|
|
}
|
|
if req.Member != nil {
|
|
srv.Member = req.Member
|
|
}
|
|
if req.Tester != nil {
|
|
srv.Tester = req.Tester
|
|
}
|
|
|
|
var resp *rpcpb.Response
|
|
resp, err = srv.handleTesterRequest(req)
|
|
if err != nil {
|
|
errc <- err
|
|
// TODO: handle error and retry
|
|
return
|
|
}
|
|
|
|
if err = stream.Send(resp); err != nil {
|
|
errc <- err
|
|
// TODO: handle error and retry
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
|
|
select {
|
|
case reterr = <-errc:
|
|
case <-stream.Context().Done():
|
|
reterr = stream.Context().Err()
|
|
}
|
|
return reterr
|
|
}
|