blob: 1625f3661b3e0f251a34b56d8dcc8ed172895ef0 [file]
/*
*
* Copyright 2020 gRPC 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 xds
import (
"errors"
"fmt"
"net"
"google.golang.org/grpc"
"google.golang.org/grpc/connectivity"
estats "google.golang.org/grpc/experimental/stats"
"google.golang.org/grpc/internal"
internalgrpclog "google.golang.org/grpc/internal/grpclog"
"google.golang.org/grpc/internal/grpcsync"
istats "google.golang.org/grpc/internal/stats"
"google.golang.org/grpc/internal/xds/bootstrap"
"google.golang.org/grpc/internal/xds/server"
"google.golang.org/grpc/internal/xds/xdsclient"
)
const serverPrefix = "[xds-server %p] "
var (
// These will be overridden in unit tests.
xdsClientPool = xdsclient.DefaultPool
newGRPCServer = func(opts ...grpc.ServerOption) grpcServer {
return grpc.NewServer(opts...)
}
)
func init() {
server.UnderlyingGRPCServer = func(s *GRPCServer) *grpc.Server {
if s == nil {
return nil
}
// Tests may replace the underlying server with a fake implementation.
gs, _ := s.gs.(*grpc.Server)
return gs
}
}
// grpcServer contains methods from grpc.Server which are used by the
// GRPCServer type here. This is useful for overriding in unit tests.
type grpcServer interface {
RegisterService(*grpc.ServiceDesc, any)
Serve(net.Listener) error
Stop()
GracefulStop()
GetServiceInfo() map[string]grpc.ServiceInfo
}
// GRPCServer wraps a gRPC server and provides server-side xDS functionality, by
// communication with a management server using xDS APIs. It implements the
// grpc.ServiceRegistrar interface and can be passed to service registration
// functions in IDL generated code.
type GRPCServer struct {
gs grpcServer
quit *grpcsync.Event
logger *internalgrpclog.PrefixLogger
opts *server.Options
xdsC xdsclient.XDSClient
xdsClientClose func()
}
// NewGRPCServer creates an xDS-enabled gRPC server using the passed in opts.
// The underlying gRPC server has no service registered and has not started to
// accept requests yet.
func NewGRPCServer(opts ...grpc.ServerOption) (*GRPCServer, error) {
// xdsFilterWrapper is a callback passed to the underlying gRPC server.
// It adapts the generic stream parameter and delegates to RouteAndProcess
// to execute xDS HTTP filter interceptors for each incoming RPC.
xDSFilterWrapper := func(ss grpc.ServerStream) (grpc.ServerStream, error) {
return server.RouteAndProcess(ss)
}
// Construct a grpc.ServerOption that registers xDSFilterWrapper on
// the server.
xDSFilterWrapperOption := internal.XDSFilterWrapperOption.(func(func(grpc.ServerStream) (grpc.ServerStream, error)) grpc.ServerOption)
newOpts := append([]grpc.ServerOption{xDSFilterWrapperOption(xDSFilterWrapper)}, opts...)
s := &GRPCServer{
gs: newGRPCServer(newOpts...),
quit: grpcsync.NewEvent(),
}
s.handleServerOptions(opts)
var mrl estats.MetricsRecorder
mrl = istats.NewMetricsRecorderList(nil)
if srv, ok := s.gs.(*grpc.Server); ok { // Will hit in prod but not for testing.
mrl = internal.MetricsRecorderForServer.(func(*grpc.Server) estats.MetricsRecorder)(srv)
}
// Initializing the xDS client upfront (instead of at serving time)
// simplifies the code by eliminating the need for a mutex to protect the
// xdsC and xdsClientClose fields.
pool := xdsClientPool
if s.opts.ClientPoolForTesting != nil {
pool = s.opts.ClientPoolForTesting
}
xdsClient, xdsClientClose, err := pool.NewClient(xdsclient.NameForServer, mrl)
if err != nil {
return nil, fmt.Errorf("xDS client creation failed: %v", err)
}
// Validate the bootstrap configuration for server specific fields.
// Listener resource name template is mandatory on the server side unless a
// listener resource name override is provided.
if s.opts.OverrideListenerResourceName == nil {
cfg := xdsClient.BootstrapConfig()
if cfg.ServerListenerResourceNameTemplate() == "" {
xdsClientClose()
return nil, errors.New("missing server_listener_resource_name_template in the bootstrap configuration")
}
}
s.xdsC = xdsClient
s.xdsClientClose = xdsClientClose
s.logger = internalgrpclog.NewPrefixLogger(logger, fmt.Sprintf(serverPrefix, s))
s.logger.Infof("Created xds.GRPCServer")
return s, nil
}
// handleServerOptions iterates through the list of server options passed in by
// the user, and handles the xDS server specific options.
func (s *GRPCServer) handleServerOptions(opts []grpc.ServerOption) {
so := s.defaultServerOptions()
server.ApplyServerOptions(opts, so)
s.opts = so
}
func (s *GRPCServer) defaultServerOptions() *server.Options {
return &server.Options{
// A default serving mode change callback which simply logs at the
// default-visible log level. This will be used if the application does not
// register a mode change callback.
//
// Note that this means that `s.opts.ModeCallback` will never be nil and can
// safely be invoked directly from `handleServingModeChanges`.
ModeCallback: s.loggingServerModeChangeCallback,
}
}
func (s *GRPCServer) loggingServerModeChangeCallback(addr net.Addr, mode connectivity.ServingMode, err error) {
switch mode {
case connectivity.ServingModeServing:
s.logger.Errorf("Listener %q entering mode: %q", addr.String(), mode)
case connectivity.ServingModeNotServing:
s.logger.Errorf("Listener %q entering mode: %q due to error: %v", addr.String(), mode, err)
}
}
// RegisterService registers a service and its implementation to the underlying
// gRPC server. It is called from the IDL generated code. This must be called
// before invoking Serve.
func (s *GRPCServer) RegisterService(sd *grpc.ServiceDesc, ss any) {
s.gs.RegisterService(sd, ss)
}
// GetServiceInfo returns a map from service names to ServiceInfo.
// Service names include the package names, in the form of <package>.<service>.
func (s *GRPCServer) GetServiceInfo() map[string]grpc.ServiceInfo {
return s.gs.GetServiceInfo()
}
// Serve gets the underlying gRPC server to accept incoming connections on the
// listener lis, which is expected to be listening on a TCP port.
//
// A connection to the management server, to receive xDS configuration, is
// initiated here.
//
// Serve will return a non-nil error unless Stop or GracefulStop is called.
func (s *GRPCServer) Serve(lis net.Listener) error {
s.logger.Infof("Serve() passed a net.Listener on %s", lis.Addr().String())
if _, ok := lis.Addr().(*net.TCPAddr); !ok {
return fmt.Errorf("xds: GRPCServer expects listener to return a net.TCPAddr. Got %T", lis.Addr())
}
if s.quit.HasFired() {
return grpc.ErrServerStopped
}
// The server listener resource name template from the bootstrap
// configuration contains a template for the name of the Listener resource
// to subscribe to for a gRPC server. If the token `%s` is present in the
// string, it will be replaced with the server's listening "IP:port" (e.g.,
// "0.0.0.0:8080", "[::]:8080").
var name string
if s.opts.OverrideListenerResourceName != nil {
name = s.opts.OverrideListenerResourceName(lis.Addr())
} else {
cfg := s.xdsC.BootstrapConfig()
name = cfg.ServerListenerResourceNameTemplate()
name = bootstrap.PopulateResourceTemplate(name, lis.Addr().String())
}
// Create a listenerWrapper which handles all functionality required by
// this particular instance of Serve().
lw := server.NewListenerWrapper(server.ListenerWrapperParams{
Listener: lis,
ListenerResourceName: name,
XDSClient: s.xdsC,
ModeCallback: s.opts.ModeCallback,
})
return s.gs.Serve(lw)
}
// Stop stops the underlying gRPC server. It immediately closes all open
// connections. It cancels all active RPCs on the server side and the
// corresponding pending RPCs on the client side will get notified by connection
// errors.
func (s *GRPCServer) Stop() {
s.quit.Fire()
s.gs.Stop()
if s.xdsC != nil {
s.xdsClientClose()
}
}
// GracefulStop stops the underlying gRPC server gracefully. It stops the server
// from accepting new connections and RPCs and blocks until all the pending RPCs
// are finished.
func (s *GRPCServer) GracefulStop() {
s.quit.Fire()
s.gs.GracefulStop()
if s.xdsC != nil {
s.xdsClientClose()
}
}