google.golang.org/grpc@v1.74.2/xds/internal/clients/xdsclient/clientimpl_watchers.go (about) 1 /* 2 * 3 * Copyright 2025 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 24 "google.golang.org/grpc/xds/internal/clients/xdsclient/internal/xdsresource" 25 ) 26 27 // wrappingWatcher is a wrapper around an xdsresource.ResourceWatcher that adds 28 // the node ID to the error messages reported to the watcher. 29 type wrappingWatcher struct { 30 ResourceWatcher 31 nodeID string 32 } 33 34 func (w *wrappingWatcher) AmbientError(err error, done func()) { 35 w.ResourceWatcher.AmbientError(fmt.Errorf("[xDS node id: %v]: %w", w.nodeID, err), done) 36 } 37 38 func (w *wrappingWatcher) ResourceError(err error, done func()) { 39 w.ResourceWatcher.ResourceError(fmt.Errorf("[xDS node id: %v]: %w", w.nodeID, err), done) 40 } 41 42 // WatchResource starts watching the specified resource. 43 // 44 // typeURL specifies the resource type implementation to use. The watch fails 45 // if there is no resource type implementation for the given typeURL. See the 46 // ResourceTypes field in the Config struct used to create the XDSClient. 47 // 48 // The returned function cancels the watch and prevents future calls to the 49 // watcher. 50 func (c *XDSClient) WatchResource(typeURL, resourceName string, watcher ResourceWatcher) (cancel func()) { 51 // Return early if the client is already closed. 52 if c.done.HasFired() { 53 logger.Warningf("Watch registered for type %q, but client is closed", typeURL) 54 return func() {} 55 } 56 57 watcher = &wrappingWatcher{ 58 ResourceWatcher: watcher, 59 nodeID: c.config.Node.ID, 60 } 61 62 rType, ok := c.config.ResourceTypes[typeURL] 63 if !ok { 64 logger.Warningf("ResourceType implementation for resource type url %v is not found", rType.TypeURL) 65 watcher.ResourceError(fmt.Errorf("ResourceType implementation for resource type url %v is not found", rType.TypeURL), func() {}) 66 return func() {} 67 } 68 69 n := xdsresource.ParseName(resourceName) 70 a := c.getAuthorityForResource(n) 71 if a == nil { 72 logger.Warningf("Watch registered for name %q of type %q, authority %q is not found", rType.TypeName, resourceName, n.Authority) 73 watcher.ResourceError(fmt.Errorf("authority %q not found in bootstrap config for resource %q", n.Authority, resourceName), func() {}) 74 return func() {} 75 } 76 // The watchResource method on the authority is invoked with n.String() 77 // instead of resourceName because n.String() canonicalizes the given name. 78 // So, two resource names which don't differ in the query string, but only 79 // differ in the order of context params will result in the same resource 80 // being watched by the authority. 81 return a.watchResource(rType, n.String(), watcher) 82 } 83 84 // Gets the authority for the given resource name. 85 // 86 // See examples in this section of the gRFC: 87 // https://github.com/grpc/proposal/blob/master/A47-xds-federation.md#bootstrap-config-changes 88 func (c *XDSClient) getAuthorityForResource(name *xdsresource.Name) *authority { 89 // For new-style resource names, always lookup the authorities map. If the 90 // name does not specify an authority, we will end up looking for an entry 91 // in the map with the empty string as the key. 92 if name.Scheme == xdsresource.FederationScheme { 93 return c.authorities[name.Authority] 94 } 95 96 // For old-style resource names, we use the top-level authority if the name 97 // does not specify an authority. 98 if name.Authority == "" { 99 return c.topLevelAuthority 100 } 101 return c.authorities[name.Authority] 102 }