google.golang.org/grpc@v1.72.2/xds/server.go (about)

     1  /*
     2   *
     3   * Copyright 2020 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   */
    18  
    19  package xds
    20  
    21  import (
    22  	"context"
    23  	"errors"
    24  	"fmt"
    25  	"net"
    26  
    27  	"google.golang.org/grpc"
    28  	"google.golang.org/grpc/codes"
    29  	"google.golang.org/grpc/connectivity"
    30  	estats "google.golang.org/grpc/experimental/stats"
    31  	"google.golang.org/grpc/internal"
    32  	internalgrpclog "google.golang.org/grpc/internal/grpclog"
    33  	"google.golang.org/grpc/internal/grpcsync"
    34  	iresolver "google.golang.org/grpc/internal/resolver"
    35  	istats "google.golang.org/grpc/internal/stats"
    36  	"google.golang.org/grpc/internal/transport"
    37  	"google.golang.org/grpc/internal/xds/bootstrap"
    38  	"google.golang.org/grpc/metadata"
    39  	"google.golang.org/grpc/status"
    40  	"google.golang.org/grpc/xds/internal/server"
    41  	"google.golang.org/grpc/xds/internal/xdsclient"
    42  	"google.golang.org/grpc/xds/internal/xdsclient/xdsresource"
    43  )
    44  
    45  const serverPrefix = "[xds-server %p] "
    46  
    47  var (
    48  	// These will be overridden in unit tests.
    49  	xdsClientPool = xdsclient.DefaultPool
    50  	newGRPCServer = func(opts ...grpc.ServerOption) grpcServer {
    51  		return grpc.NewServer(opts...)
    52  	}
    53  )
    54  
    55  // grpcServer contains methods from grpc.Server which are used by the
    56  // GRPCServer type here. This is useful for overriding in unit tests.
    57  type grpcServer interface {
    58  	RegisterService(*grpc.ServiceDesc, any)
    59  	Serve(net.Listener) error
    60  	Stop()
    61  	GracefulStop()
    62  	GetServiceInfo() map[string]grpc.ServiceInfo
    63  }
    64  
    65  // GRPCServer wraps a gRPC server and provides server-side xDS functionality, by
    66  // communication with a management server using xDS APIs. It implements the
    67  // grpc.ServiceRegistrar interface and can be passed to service registration
    68  // functions in IDL generated code.
    69  type GRPCServer struct {
    70  	gs             grpcServer
    71  	quit           *grpcsync.Event
    72  	logger         *internalgrpclog.PrefixLogger
    73  	opts           *serverOptions
    74  	xdsC           xdsclient.XDSClient
    75  	xdsClientClose func()
    76  }
    77  
    78  // NewGRPCServer creates an xDS-enabled gRPC server using the passed in opts.
    79  // The underlying gRPC server has no service registered and has not started to
    80  // accept requests yet.
    81  func NewGRPCServer(opts ...grpc.ServerOption) (*GRPCServer, error) {
    82  	newOpts := []grpc.ServerOption{
    83  		grpc.ChainUnaryInterceptor(xdsUnaryInterceptor),
    84  		grpc.ChainStreamInterceptor(xdsStreamInterceptor),
    85  	}
    86  	newOpts = append(newOpts, opts...)
    87  	s := &GRPCServer{
    88  		gs:   newGRPCServer(newOpts...),
    89  		quit: grpcsync.NewEvent(),
    90  	}
    91  	s.handleServerOptions(opts)
    92  
    93  	var mrl estats.MetricsRecorder
    94  	mrl = istats.NewMetricsRecorderList(nil)
    95  	if srv, ok := s.gs.(*grpc.Server); ok { // Will hit in prod but not for testing.
    96  		mrl = internal.MetricsRecorderForServer.(func(*grpc.Server) estats.MetricsRecorder)(srv)
    97  	}
    98  
    99  	// Initializing the xDS client upfront (instead of at serving time)
   100  	// simplifies the code by eliminating the need for a mutex to protect the
   101  	// xdsC and xdsClientClose fields.
   102  	pool := xdsClientPool
   103  	if s.opts.clientPoolForTesting != nil {
   104  		pool = s.opts.clientPoolForTesting
   105  	}
   106  	xdsClient, xdsClientClose, err := pool.NewClient(xdsclient.NameForServer, mrl)
   107  	if err != nil {
   108  		return nil, fmt.Errorf("xDS client creation failed: %v", err)
   109  	}
   110  
   111  	// Validate the bootstrap configuration for server specific fields.
   112  
   113  	// Listener resource name template is mandatory on the server side.
   114  	cfg := xdsClient.BootstrapConfig()
   115  	if cfg.ServerListenerResourceNameTemplate() == "" {
   116  		xdsClientClose()
   117  		return nil, errors.New("missing server_listener_resource_name_template in the bootstrap configuration")
   118  	}
   119  
   120  	s.xdsC = xdsClient
   121  	s.xdsClientClose = xdsClientClose
   122  
   123  	s.logger = internalgrpclog.NewPrefixLogger(logger, fmt.Sprintf(serverPrefix, s))
   124  	s.logger.Infof("Created xds.GRPCServer")
   125  
   126  	return s, nil
   127  }
   128  
   129  // handleServerOptions iterates through the list of server options passed in by
   130  // the user, and handles the xDS server specific options.
   131  func (s *GRPCServer) handleServerOptions(opts []grpc.ServerOption) {
   132  	so := s.defaultServerOptions()
   133  	for _, opt := range opts {
   134  		if o, ok := opt.(*serverOption); ok {
   135  			o.apply(so)
   136  		}
   137  	}
   138  	s.opts = so
   139  }
   140  
   141  func (s *GRPCServer) defaultServerOptions() *serverOptions {
   142  	return &serverOptions{
   143  		// A default serving mode change callback which simply logs at the
   144  		// default-visible log level. This will be used if the application does not
   145  		// register a mode change callback.
   146  		//
   147  		// Note that this means that `s.opts.modeCallback` will never be nil and can
   148  		// safely be invoked directly from `handleServingModeChanges`.
   149  		modeCallback: s.loggingServerModeChangeCallback,
   150  	}
   151  }
   152  
   153  func (s *GRPCServer) loggingServerModeChangeCallback(addr net.Addr, args ServingModeChangeArgs) {
   154  	switch args.Mode {
   155  	case connectivity.ServingModeServing:
   156  		s.logger.Errorf("Listener %q entering mode: %q", addr.String(), args.Mode)
   157  	case connectivity.ServingModeNotServing:
   158  		s.logger.Errorf("Listener %q entering mode: %q due to error: %v", addr.String(), args.Mode, args.Err)
   159  	}
   160  }
   161  
   162  // RegisterService registers a service and its implementation to the underlying
   163  // gRPC server. It is called from the IDL generated code. This must be called
   164  // before invoking Serve.
   165  func (s *GRPCServer) RegisterService(sd *grpc.ServiceDesc, ss any) {
   166  	s.gs.RegisterService(sd, ss)
   167  }
   168  
   169  // GetServiceInfo returns a map from service names to ServiceInfo.
   170  // Service names include the package names, in the form of <package>.<service>.
   171  func (s *GRPCServer) GetServiceInfo() map[string]grpc.ServiceInfo {
   172  	return s.gs.GetServiceInfo()
   173  }
   174  
   175  // Serve gets the underlying gRPC server to accept incoming connections on the
   176  // listener lis, which is expected to be listening on a TCP port.
   177  //
   178  // A connection to the management server, to receive xDS configuration, is
   179  // initiated here.
   180  //
   181  // Serve will return a non-nil error unless Stop or GracefulStop is called.
   182  func (s *GRPCServer) Serve(lis net.Listener) error {
   183  	s.logger.Infof("Serve() passed a net.Listener on %s", lis.Addr().String())
   184  	if _, ok := lis.Addr().(*net.TCPAddr); !ok {
   185  		return fmt.Errorf("xds: GRPCServer expects listener to return a net.TCPAddr. Got %T", lis.Addr())
   186  	}
   187  
   188  	if s.quit.HasFired() {
   189  		return grpc.ErrServerStopped
   190  	}
   191  
   192  	// The server listener resource name template from the bootstrap
   193  	// configuration contains a template for the name of the Listener resource
   194  	// to subscribe to for a gRPC server. If the token `%s` is present in the
   195  	// string, it will be replaced with the server's listening "IP:port" (e.g.,
   196  	// "0.0.0.0:8080", "[::]:8080").
   197  	cfg := s.xdsC.BootstrapConfig()
   198  	name := bootstrap.PopulateResourceTemplate(cfg.ServerListenerResourceNameTemplate(), lis.Addr().String())
   199  
   200  	// Create a listenerWrapper which handles all functionality required by
   201  	// this particular instance of Serve().
   202  	lw := server.NewListenerWrapper(server.ListenerWrapperParams{
   203  		Listener:             lis,
   204  		ListenerResourceName: name,
   205  		XDSClient:            s.xdsC,
   206  		ModeCallback: func(addr net.Addr, mode connectivity.ServingMode, err error) {
   207  			s.opts.modeCallback(addr, ServingModeChangeArgs{
   208  				Mode: mode,
   209  				Err:  err,
   210  			})
   211  		},
   212  	})
   213  	return s.gs.Serve(lw)
   214  }
   215  
   216  // Stop stops the underlying gRPC server. It immediately closes all open
   217  // connections. It cancels all active RPCs on the server side and the
   218  // corresponding pending RPCs on the client side will get notified by connection
   219  // errors.
   220  func (s *GRPCServer) Stop() {
   221  	s.quit.Fire()
   222  	s.gs.Stop()
   223  	if s.xdsC != nil {
   224  		s.xdsClientClose()
   225  	}
   226  }
   227  
   228  // GracefulStop stops the underlying gRPC server gracefully. It stops the server
   229  // from accepting new connections and RPCs and blocks until all the pending RPCs
   230  // are finished.
   231  func (s *GRPCServer) GracefulStop() {
   232  	s.quit.Fire()
   233  	s.gs.GracefulStop()
   234  	if s.xdsC != nil {
   235  		s.xdsClientClose()
   236  	}
   237  }
   238  
   239  // routeAndProcess routes the incoming RPC to a configured route in the route
   240  // table and also processes the RPC by running the incoming RPC through any HTTP
   241  // Filters configured.
   242  func routeAndProcess(ctx context.Context) error {
   243  	conn := transport.GetConnection(ctx)
   244  	cw, ok := conn.(interface {
   245  		UsableRouteConfiguration() xdsresource.UsableRouteConfiguration
   246  	})
   247  	if !ok {
   248  		return errors.New("missing virtual hosts in incoming context")
   249  	}
   250  
   251  	rc := cw.UsableRouteConfiguration()
   252  	// Error out at routing l7 level with a status code UNAVAILABLE, represents
   253  	// an nack before usable route configuration or resource not found for RDS
   254  	// or error combining LDS + RDS (Shouldn't happen).
   255  	if rc.Err != nil {
   256  		if logger.V(2) {
   257  			logger.Infof("RPC on connection with xDS Configuration error: %v", rc.Err)
   258  		}
   259  		return status.Error(codes.Unavailable, fmt.Sprintf("error from xDS configuration for matched route configuration: %v", rc.Err))
   260  	}
   261  
   262  	mn, ok := grpc.Method(ctx)
   263  	if !ok {
   264  		return errors.New("missing method name in incoming context")
   265  	}
   266  	md, ok := metadata.FromIncomingContext(ctx)
   267  	if !ok {
   268  		return errors.New("missing metadata in incoming context")
   269  	}
   270  	// A41 added logic to the core grpc implementation to guarantee that once
   271  	// the RPC gets to this point, there will be a single, unambiguous authority
   272  	// present in the header map.
   273  	authority := md.Get(":authority")
   274  	vh := xdsresource.FindBestMatchingVirtualHostServer(authority[0], rc.VHS)
   275  	if vh == nil {
   276  		return rc.StatusErrWithNodeID(codes.Unavailable, "the incoming RPC did not match a configured Virtual Host")
   277  	}
   278  
   279  	var rwi *xdsresource.RouteWithInterceptors
   280  	rpcInfo := iresolver.RPCInfo{
   281  		Context: ctx,
   282  		Method:  mn,
   283  	}
   284  	for _, r := range vh.Routes {
   285  		if r.M.Match(rpcInfo) {
   286  			// "NonForwardingAction is expected for all Routes used on
   287  			// server-side; a route with an inappropriate action causes RPCs
   288  			// matching that route to fail with UNAVAILABLE." - A36
   289  			if r.ActionType != xdsresource.RouteActionNonForwardingAction {
   290  				return rc.StatusErrWithNodeID(codes.Unavailable, "the incoming RPC matched to a route that was not of action type non forwarding")
   291  			}
   292  			rwi = &r
   293  			break
   294  		}
   295  	}
   296  	if rwi == nil {
   297  		return rc.StatusErrWithNodeID(codes.Unavailable, "the incoming RPC did not match a configured Route")
   298  	}
   299  	for _, interceptor := range rwi.Interceptors {
   300  		if err := interceptor.AllowRPC(ctx); err != nil {
   301  			return rc.StatusErrWithNodeID(codes.PermissionDenied, "Incoming RPC is not allowed: %v", err)
   302  		}
   303  	}
   304  	return nil
   305  }
   306  
   307  // xdsUnaryInterceptor is the unary interceptor added to the gRPC server to
   308  // perform any xDS specific functionality on unary RPCs.
   309  func xdsUnaryInterceptor(ctx context.Context, req any, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) {
   310  	if err := routeAndProcess(ctx); err != nil {
   311  		return nil, err
   312  	}
   313  	return handler(ctx, req)
   314  }
   315  
   316  // xdsStreamInterceptor is the stream interceptor added to the gRPC server to
   317  // perform any xDS specific functionality on streaming RPCs.
   318  func xdsStreamInterceptor(srv any, ss grpc.ServerStream, _ *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
   319  	if err := routeAndProcess(ss.Context()); err != nil {
   320  		return err
   321  	}
   322  	return handler(srv, ss)
   323  }