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 }