google.golang.org/grpc@v1.74.2/xds/internal/xdsclient/clientimpl.go (about) 1 /* 2 * 3 * Copyright 2022 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 xdsclient 20 21 import ( 22 "fmt" 23 "sync/atomic" 24 "time" 25 26 "google.golang.org/grpc" 27 estats "google.golang.org/grpc/experimental/stats" 28 "google.golang.org/grpc/internal/backoff" 29 "google.golang.org/grpc/internal/grpclog" 30 "google.golang.org/grpc/internal/xds/bootstrap" 31 32 xdsbootstrap "google.golang.org/grpc/xds/bootstrap" 33 "google.golang.org/grpc/xds/internal/clients" 34 "google.golang.org/grpc/xds/internal/clients/grpctransport" 35 "google.golang.org/grpc/xds/internal/clients/lrsclient" 36 "google.golang.org/grpc/xds/internal/clients/xdsclient" 37 "google.golang.org/grpc/xds/internal/clients/xdsclient/metrics" 38 ) 39 40 const ( 41 // NameForServer represents the value to be passed as name when creating an xDS 42 // client from xDS-enabled gRPC servers. This is a well-known dedicated key 43 // value, and is defined in gRFC A71. 44 NameForServer = "#server" 45 46 defaultWatchExpiryTimeout = 15 * time.Second 47 ) 48 49 var ( 50 // The following functions are no-ops in the actual code, but can be 51 // overridden in tests to give them visibility into certain events. 52 xdsClientImplCreateHook = func(string) {} 53 xdsClientImplCloseHook = func(string) {} 54 55 defaultExponentialBackoff = backoff.DefaultExponential.Backoff 56 57 xdsClientResourceUpdatesValidMetric = estats.RegisterInt64Count(estats.MetricDescriptor{ 58 Name: "grpc.xds_client.resource_updates_valid", 59 Description: "A counter of resources received that were considered valid. The counter will be incremented even for resources that have not changed.", 60 Unit: "resource", 61 Labels: []string{"grpc.target", "grpc.xds.server", "grpc.xds.resource_type"}, 62 Default: false, 63 }) 64 xdsClientResourceUpdatesInvalidMetric = estats.RegisterInt64Count(estats.MetricDescriptor{ 65 Name: "grpc.xds_client.resource_updates_invalid", 66 Description: "A counter of resources received that were considered invalid.", 67 Unit: "resource", 68 Labels: []string{"grpc.target", "grpc.xds.server", "grpc.xds.resource_type"}, 69 Default: false, 70 }) 71 xdsClientServerFailureMetric = estats.RegisterInt64Count(estats.MetricDescriptor{ 72 Name: "grpc.xds_client.server_failure", 73 Description: "A counter of xDS servers going from healthy to unhealthy. A server goes unhealthy when we have a connectivity failure or when the ADS stream fails without seeing a response message, as per gRFC A57.", 74 Unit: "failure", 75 Labels: []string{"grpc.target", "grpc.xds.server"}, 76 Default: false, 77 }) 78 ) 79 80 // clientImpl embed xdsclient.XDSClient and implement internal XDSClient 81 // interface with ref counting so that it can be shared by the xds resolver and 82 // balancer implementations, across multiple ClientConns and Servers. 83 type clientImpl struct { 84 *xdsclient.XDSClient // TODO: #8313 - get rid of embedding, if possible. 85 86 // The following fields are initialized at creation time and are read-only 87 // after that. 88 xdsClientConfig xdsclient.Config 89 bootstrapConfig *bootstrap.Config 90 logger *grpclog.PrefixLogger 91 target string 92 lrsClient *lrsclient.LRSClient 93 94 // Accessed atomically 95 refCount int32 96 } 97 98 // metricsReporter implements the clients.MetricsReporter interface and uses an 99 // underlying stats.MetricsRecorderList to record metrics. 100 type metricsReporter struct { 101 recorder estats.MetricsRecorder 102 target string 103 } 104 105 // ReportMetric implements the clients.MetricsReporter interface. 106 // It receives metric data, determines the appropriate metric based on the type 107 // of the data, and records it using the embedded MetricsRecorderList. 108 func (mr *metricsReporter) ReportMetric(metric any) { 109 if mr.recorder == nil { 110 return 111 } 112 113 switch m := metric.(type) { 114 case *metrics.ResourceUpdateValid: 115 xdsClientResourceUpdatesValidMetric.Record(mr.recorder, 1, mr.target, m.ServerURI, m.ResourceType) 116 case *metrics.ResourceUpdateInvalid: 117 xdsClientResourceUpdatesInvalidMetric.Record(mr.recorder, 1, mr.target, m.ServerURI, m.ResourceType) 118 case *metrics.ServerFailure: 119 xdsClientServerFailureMetric.Record(mr.recorder, 1, mr.target, m.ServerURI) 120 } 121 } 122 123 func newClientImpl(config *bootstrap.Config, metricsRecorder estats.MetricsRecorder, target string) (*clientImpl, error) { 124 gConfig, err := buildXDSClientConfig(config, metricsRecorder, target) 125 if err != nil { 126 return nil, err 127 } 128 client, err := xdsclient.New(gConfig) 129 if err != nil { 130 return nil, err 131 } 132 c := &clientImpl{XDSClient: client, xdsClientConfig: gConfig, bootstrapConfig: config, target: target, refCount: 1} 133 c.logger = prefixLogger(c) 134 return c, nil 135 } 136 137 // BootstrapConfig returns the configuration read from the bootstrap file. 138 // Callers must treat the return value as read-only. 139 func (c *clientImpl) BootstrapConfig() *bootstrap.Config { 140 return c.bootstrapConfig 141 } 142 143 func (c *clientImpl) incrRef() int32 { 144 return atomic.AddInt32(&c.refCount, 1) 145 } 146 147 func (c *clientImpl) decrRef() int32 { 148 return atomic.AddInt32(&c.refCount, -1) 149 } 150 151 // buildXDSClientConfig builds the xdsclient.Config from the bootstrap.Config. 152 func buildXDSClientConfig(config *bootstrap.Config, metricsRecorder estats.MetricsRecorder, target string) (xdsclient.Config, error) { 153 grpcTransportConfigs := make(map[string]grpctransport.Config) 154 gServerCfgMap := make(map[xdsclient.ServerConfig]*bootstrap.ServerConfig) 155 156 gAuthorities := make(map[string]xdsclient.Authority) 157 for name, cfg := range config.Authorities() { 158 // If server configs are specified in the authorities map, use that. 159 // Else, use the top-level server configs. 160 serverCfg := config.XDSServers() 161 if len(cfg.XDSServers) >= 1 { 162 serverCfg = cfg.XDSServers 163 } 164 var gServerCfg []xdsclient.ServerConfig 165 for _, sc := range serverCfg { 166 if err := populateGRPCTransportConfigsFromServerConfig(sc, grpcTransportConfigs); err != nil { 167 return xdsclient.Config{}, err 168 } 169 gsc := xdsclient.ServerConfig{ 170 ServerIdentifier: clients.ServerIdentifier{ServerURI: sc.ServerURI(), Extensions: grpctransport.ServerIdentifierExtension{ConfigName: sc.SelectedCreds().Type}}, 171 IgnoreResourceDeletion: sc.ServerFeaturesIgnoreResourceDeletion()} 172 gServerCfg = append(gServerCfg, gsc) 173 gServerCfgMap[gsc] = sc 174 } 175 gAuthorities[name] = xdsclient.Authority{XDSServers: gServerCfg} 176 } 177 178 gServerCfgs := make([]xdsclient.ServerConfig, 0, len(config.XDSServers())) 179 for _, sc := range config.XDSServers() { 180 if err := populateGRPCTransportConfigsFromServerConfig(sc, grpcTransportConfigs); err != nil { 181 return xdsclient.Config{}, err 182 } 183 gsc := xdsclient.ServerConfig{ 184 ServerIdentifier: clients.ServerIdentifier{ServerURI: sc.ServerURI(), Extensions: grpctransport.ServerIdentifierExtension{ConfigName: sc.SelectedCreds().Type}}, 185 IgnoreResourceDeletion: sc.ServerFeaturesIgnoreResourceDeletion()} 186 gServerCfgs = append(gServerCfgs, gsc) 187 gServerCfgMap[gsc] = sc 188 } 189 190 node := config.Node() 191 gNode := clients.Node{ 192 ID: node.GetId(), 193 Cluster: node.GetCluster(), 194 Metadata: node.Metadata, 195 UserAgentName: node.UserAgentName, 196 UserAgentVersion: node.GetUserAgentVersion(), 197 } 198 if node.Locality != nil { 199 gNode.Locality = clients.Locality{ 200 Region: node.Locality.Region, 201 Zone: node.Locality.Zone, 202 SubZone: node.Locality.SubZone, 203 } 204 } 205 206 return xdsclient.Config{ 207 Authorities: gAuthorities, 208 Servers: gServerCfgs, 209 Node: gNode, 210 TransportBuilder: grpctransport.NewBuilder(grpcTransportConfigs), 211 ResourceTypes: supportedResourceTypes(config, gServerCfgMap), 212 MetricsReporter: &metricsReporter{recorder: metricsRecorder, target: target}, 213 }, nil 214 } 215 216 // populateGRPCTransportConfigsFromServerConfig iterates through the channel 217 // credentials of the provided server configuration, builds credential bundles, 218 // and populates the grpctransport.Config map. 219 func populateGRPCTransportConfigsFromServerConfig(sc *bootstrap.ServerConfig, grpcTransportConfigs map[string]grpctransport.Config) error { 220 for _, cc := range sc.ChannelCreds() { 221 c := xdsbootstrap.GetCredentials(cc.Type) 222 if c == nil { 223 continue 224 } 225 bundle, _, err := c.Build(cc.Config) 226 if err != nil { 227 return fmt.Errorf("xds: failed to build credentials bundle from bootstrap for %q: %v", cc.Type, err) 228 } 229 grpcTransportConfigs[cc.Type] = grpctransport.Config{ 230 Credentials: bundle, 231 GRPCNewClient: func(target string, opts ...grpc.DialOption) (*grpc.ClientConn, error) { 232 opts = append(opts, sc.DialOptions()...) 233 return grpc.NewClient(target, opts...) 234 }, 235 } 236 } 237 return nil 238 }