google.golang.org/grpc@v1.72.2/xds/internal/balancer/clusterresolver/resource_resolver_dns.go (about)

     1  /*
     2   *
     3   * Copyright 2021 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 clusterresolver
    20  
    21  import (
    22  	"fmt"
    23  	"net/url"
    24  	"sync"
    25  
    26  	"google.golang.org/grpc/internal/grpclog"
    27  	"google.golang.org/grpc/internal/pretty"
    28  	"google.golang.org/grpc/resolver"
    29  	"google.golang.org/grpc/serviceconfig"
    30  )
    31  
    32  var (
    33  	newDNS = func(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
    34  		// The dns resolver is registered by the grpc package. So, this call to
    35  		// resolver.Get() is never expected to return nil.
    36  		return resolver.Get("dns").Build(target, cc, opts)
    37  	}
    38  )
    39  
    40  // dnsDiscoveryMechanism watches updates for the given DNS hostname.
    41  //
    42  // It implements resolver.ClientConn interface to work with the DNS resolver.
    43  type dnsDiscoveryMechanism struct {
    44  	target           string
    45  	topLevelResolver topLevelResolver
    46  	dnsR             resolver.Resolver
    47  	logger           *grpclog.PrefixLogger
    48  
    49  	mu             sync.Mutex
    50  	endpoints      []resolver.Endpoint
    51  	updateReceived bool
    52  }
    53  
    54  // newDNSResolver creates an endpoints resolver which uses a DNS resolver under
    55  // the hood.
    56  //
    57  // An error in parsing the provided target string or an error in creating a DNS
    58  // resolver means that we will never be able to resolve the provided target
    59  // strings to endpoints. The topLevelResolver propagates address updates to the
    60  // clusterresolver LB policy **only** after it receives updates from all its
    61  // child resolvers. Therefore, an error here means that the topLevelResolver
    62  // will never send address updates to the clusterresolver LB policy.
    63  //
    64  // Calling the onError() callback will ensure that this error is
    65  // propagated to the child policy which eventually move the channel to
    66  // transient failure.
    67  //
    68  // The `dnsR` field is unset if we run into errors in this function. Therefore, a
    69  // nil check is required wherever we access that field.
    70  func newDNSResolver(target string, topLevelResolver topLevelResolver, logger *grpclog.PrefixLogger) *dnsDiscoveryMechanism {
    71  	ret := &dnsDiscoveryMechanism{
    72  		target:           target,
    73  		topLevelResolver: topLevelResolver,
    74  		logger:           logger,
    75  	}
    76  	u, err := url.Parse("dns:///" + target)
    77  	if err != nil {
    78  		if ret.logger.V(2) {
    79  			ret.logger.Infof("Failed to parse dns hostname %q in clusterresolver LB policy", target)
    80  		}
    81  		ret.updateReceived = true
    82  		ret.topLevelResolver.onUpdate(func() {})
    83  		return ret
    84  	}
    85  
    86  	r, err := newDNS(resolver.Target{URL: *u}, ret, resolver.BuildOptions{})
    87  	if err != nil {
    88  		if ret.logger.V(2) {
    89  			ret.logger.Infof("Failed to build DNS resolver for target %q: %v", target, err)
    90  		}
    91  		ret.updateReceived = true
    92  		ret.topLevelResolver.onUpdate(func() {})
    93  		return ret
    94  	}
    95  	ret.dnsR = r
    96  	return ret
    97  }
    98  
    99  func (dr *dnsDiscoveryMechanism) lastUpdate() (any, bool) {
   100  	dr.mu.Lock()
   101  	defer dr.mu.Unlock()
   102  
   103  	if !dr.updateReceived {
   104  		return nil, false
   105  	}
   106  	return dr.endpoints, true
   107  }
   108  
   109  func (dr *dnsDiscoveryMechanism) resolveNow() {
   110  	if dr.dnsR != nil {
   111  		dr.dnsR.ResolveNow(resolver.ResolveNowOptions{})
   112  	}
   113  }
   114  
   115  // The definition of stop() mentions that implementations must not invoke any
   116  // methods on the topLevelResolver once the call to `stop()` returns. The
   117  // underlying dns resolver does not send any updates to the resolver.ClientConn
   118  // interface passed to it (implemented by dnsDiscoveryMechanism in this case)
   119  // after its `Close()` returns. Therefore, we can guarantee that no methods of
   120  // the topLevelResolver are invoked after we return from this method.
   121  func (dr *dnsDiscoveryMechanism) stop() {
   122  	if dr.dnsR != nil {
   123  		dr.dnsR.Close()
   124  	}
   125  }
   126  
   127  // dnsDiscoveryMechanism needs to implement resolver.ClientConn interface to receive
   128  // updates from the real DNS resolver.
   129  
   130  func (dr *dnsDiscoveryMechanism) UpdateState(state resolver.State) error {
   131  	if dr.logger.V(2) {
   132  		dr.logger.Infof("DNS discovery mechanism for resource %q reported an update: %s", dr.target, pretty.ToJSON(state))
   133  	}
   134  
   135  	dr.mu.Lock()
   136  	var endpoints = state.Endpoints
   137  	if len(endpoints) == 0 {
   138  		endpoints = make([]resolver.Endpoint, len(state.Addresses))
   139  		for i, a := range state.Addresses {
   140  			endpoints[i] = resolver.Endpoint{Addresses: []resolver.Address{a}}
   141  			endpoints[i].Attributes = a.BalancerAttributes
   142  		}
   143  	}
   144  	dr.endpoints = endpoints
   145  	dr.updateReceived = true
   146  	dr.mu.Unlock()
   147  
   148  	dr.topLevelResolver.onUpdate(func() {})
   149  	return nil
   150  }
   151  
   152  func (dr *dnsDiscoveryMechanism) ReportError(err error) {
   153  	if dr.logger.V(2) {
   154  		dr.logger.Infof("DNS discovery mechanism for resource %q reported error: %v", dr.target, err)
   155  	}
   156  
   157  	dr.mu.Lock()
   158  	// If a previous good update was received, suppress the error and continue
   159  	// using the previous update. If RPCs were succeeding prior to this, they
   160  	// will continue to do so. Also suppress errors if we previously received an
   161  	// error, since there will be no downstream effects of propagating this
   162  	// error.
   163  	if dr.updateReceived {
   164  		dr.mu.Unlock()
   165  		return
   166  	}
   167  	dr.endpoints = nil
   168  	dr.updateReceived = true
   169  	dr.mu.Unlock()
   170  
   171  	dr.topLevelResolver.onUpdate(func() {})
   172  }
   173  
   174  func (dr *dnsDiscoveryMechanism) NewAddress(addresses []resolver.Address) {
   175  	dr.UpdateState(resolver.State{Addresses: addresses})
   176  }
   177  
   178  func (dr *dnsDiscoveryMechanism) ParseServiceConfig(string) *serviceconfig.ParseResult {
   179  	return &serviceconfig.ParseResult{Err: fmt.Errorf("service config not supported")}
   180  }