...

Source file src/google.golang.org/grpc/internal/resolver/dns/dns_resolver_test.go

Documentation: google.golang.org/grpc/internal/resolver/dns

     1  /*
     2   *
     3   * Copyright 2018 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 dns_test
    20  
    21  import (
    22  	"context"
    23  	"errors"
    24  	"fmt"
    25  	"net"
    26  	"strings"
    27  	"sync/atomic"
    28  	"testing"
    29  	"time"
    30  
    31  	"github.com/google/go-cmp/cmp"
    32  	"github.com/google/go-cmp/cmp/cmpopts"
    33  	"google.golang.org/grpc/balancer"
    34  	grpclbstate "google.golang.org/grpc/balancer/grpclb/state"
    35  	"google.golang.org/grpc/internal"
    36  	"google.golang.org/grpc/internal/envconfig"
    37  	"google.golang.org/grpc/internal/grpctest"
    38  	"google.golang.org/grpc/internal/resolver/dns"
    39  	dnsinternal "google.golang.org/grpc/internal/resolver/dns/internal"
    40  	"google.golang.org/grpc/internal/testutils"
    41  	"google.golang.org/grpc/resolver"
    42  	dnspublic "google.golang.org/grpc/resolver/dns"
    43  	"google.golang.org/grpc/serviceconfig"
    44  
    45  	_ "google.golang.org/grpc" // To initialize internal.ParseServiceConfig
    46  )
    47  
    48  const (
    49  	txtBytesLimit           = 255
    50  	defaultTestTimeout      = 10 * time.Second
    51  	defaultTestShortTimeout = 10 * time.Millisecond
    52  
    53  	colonDefaultPort = ":443"
    54  )
    55  
    56  type s struct {
    57  	grpctest.Tester
    58  }
    59  
    60  func Test(t *testing.T) {
    61  	grpctest.RunSubTests(t, s{})
    62  }
    63  
    64  // Override the default net.Resolver with a test resolver.
    65  func overrideNetResolver(t *testing.T, r *testNetResolver) {
    66  	origNetResolver := dnsinternal.NewNetResolver
    67  	dnsinternal.NewNetResolver = func(string) (dnsinternal.NetResolver, error) { return r, nil }
    68  	t.Cleanup(func() { dnsinternal.NewNetResolver = origNetResolver })
    69  }
    70  
    71  // Override the DNS minimum resolution interval used by the resolver.
    72  func overrideResolutionInterval(t *testing.T, d time.Duration) {
    73  	origMinResInterval := dns.MinResolutionInterval
    74  	dnspublic.SetMinResolutionInterval(d)
    75  	t.Cleanup(func() { dnspublic.SetMinResolutionInterval(origMinResInterval) })
    76  }
    77  
    78  // Override the timer used by the DNS resolver to fire after a duration of d.
    79  func overrideTimeAfterFunc(t *testing.T, d time.Duration) {
    80  	origTimeAfter := dnsinternal.TimeAfterFunc
    81  	dnsinternal.TimeAfterFunc = func(time.Duration) <-chan time.Time {
    82  		return time.After(d)
    83  	}
    84  	t.Cleanup(func() { dnsinternal.TimeAfterFunc = origTimeAfter })
    85  }
    86  
    87  // Override the timer used by the DNS resolver as follows:
    88  // - use the durChan to read the duration that the resolver wants to wait for
    89  // - use the timerChan to unblock the wait on the timer
    90  func overrideTimeAfterFuncWithChannel(t *testing.T) (durChan chan time.Duration, timeChan chan time.Time) {
    91  	origTimeAfter := dnsinternal.TimeAfterFunc
    92  	durChan = make(chan time.Duration, 1)
    93  	timeChan = make(chan time.Time)
    94  	dnsinternal.TimeAfterFunc = func(d time.Duration) <-chan time.Time {
    95  		select {
    96  		case durChan <- d:
    97  		default:
    98  		}
    99  		return timeChan
   100  	}
   101  	t.Cleanup(func() { dnsinternal.TimeAfterFunc = origTimeAfter })
   102  	return durChan, timeChan
   103  }
   104  
   105  func enableSRVLookups(t *testing.T) {
   106  	origEnableSRVLookups := dns.EnableSRVLookups
   107  	dns.EnableSRVLookups = true
   108  	t.Cleanup(func() { dns.EnableSRVLookups = origEnableSRVLookups })
   109  }
   110  
   111  // Builds a DNS resolver for target and returns a couple of channels to read the
   112  // state and error pushed by the resolver respectively.
   113  func buildResolverWithTestClientConn(t *testing.T, target string) (resolver.Resolver, chan resolver.State, chan error) {
   114  	t.Helper()
   115  
   116  	b := resolver.Get("dns")
   117  	if b == nil {
   118  		t.Fatalf("Resolver for dns:/// scheme not registered")
   119  	}
   120  
   121  	stateCh := make(chan resolver.State, 1)
   122  	updateStateF := func(s resolver.State) error {
   123  		select {
   124  		case stateCh <- s:
   125  		default:
   126  		}
   127  		return nil
   128  	}
   129  
   130  	errCh := make(chan error, 1)
   131  	reportErrorF := func(err error) {
   132  		select {
   133  		case errCh <- err:
   134  		default:
   135  		}
   136  	}
   137  
   138  	tcc := &testutils.ResolverClientConn{Logger: t, UpdateStateF: updateStateF, ReportErrorF: reportErrorF}
   139  	r, err := b.Build(resolver.Target{URL: *testutils.MustParseURL(fmt.Sprintf("dns:///%s", target))}, tcc, resolver.BuildOptions{})
   140  	if err != nil {
   141  		t.Fatalf("Failed to build DNS resolver for target %q: %v\n", target, err)
   142  	}
   143  	t.Cleanup(func() { r.Close() })
   144  
   145  	return r, stateCh, errCh
   146  }
   147  
   148  // Waits for a state update from the DNS resolver and verifies the following:
   149  // - wantAddrs matches the list of addresses in the update
   150  // - wantBalancerAddrs matches the list of grpclb addresses in the update
   151  // - wantSC matches the service config in the update
   152  func verifyUpdateFromResolver(ctx context.Context, t *testing.T, stateCh chan resolver.State, wantAddrs, wantBalancerAddrs []resolver.Address, wantSC string) {
   153  	t.Helper()
   154  
   155  	var state resolver.State
   156  	select {
   157  	case <-ctx.Done():
   158  		t.Fatal("Timeout when waiting for a state update from the resolver")
   159  	case state = <-stateCh:
   160  	}
   161  
   162  	if !cmp.Equal(state.Addresses, wantAddrs, cmpopts.EquateEmpty()) {
   163  		t.Fatalf("Got addresses: %+v, want: %+v", state.Addresses, wantAddrs)
   164  	}
   165  	if gs := grpclbstate.Get(state); gs == nil {
   166  		if len(wantBalancerAddrs) > 0 {
   167  			t.Fatalf("Got no grpclb addresses. Want %d", len(wantBalancerAddrs))
   168  		}
   169  	} else {
   170  		if !cmp.Equal(gs.BalancerAddresses, wantBalancerAddrs) {
   171  			t.Fatalf("Got grpclb addresses %+v, want %+v", gs.BalancerAddresses, wantBalancerAddrs)
   172  		}
   173  	}
   174  	if wantSC == "{}" {
   175  		if state.ServiceConfig != nil && state.ServiceConfig.Config != nil {
   176  			t.Fatalf("Got service config:\n%s \nWant service config: {}", cmp.Diff(nil, state.ServiceConfig.Config))
   177  		}
   178  
   179  	} else if wantSC != "" {
   180  		wantSCParsed := internal.ParseServiceConfig.(func(string) *serviceconfig.ParseResult)(wantSC)
   181  		if !internal.EqualServiceConfigForTesting(state.ServiceConfig.Config, wantSCParsed.Config) {
   182  			t.Fatalf("Got service config:\n%s \nWant service config:\n%s", cmp.Diff(nil, state.ServiceConfig.Config), cmp.Diff(nil, wantSCParsed.Config))
   183  		}
   184  	}
   185  }
   186  
   187  // This is the service config used by the fake net.Resolver in its TXT record.
   188  //   - it contains an array of 5 entries
   189  //   - the first three will be dropped by the DNS resolver as part of its
   190  //     canarying rule matching functionality:
   191  //   - the client language does not match in the first entry
   192  //   - the percentage is set to 0 in the second entry
   193  //   - the client host name does not match in the third entry
   194  //   - the fourth and fifth entries will match the canarying rules, and therefore
   195  //     the fourth entry will be used as it will be  the first matching entry.
   196  const txtRecordGood = `
   197  [
   198  	{
   199  		"clientLanguage": [
   200  			"CPP",
   201  			"JAVA"
   202  		],
   203  		"serviceConfig": {
   204  			"loadBalancingPolicy": "grpclb",
   205  			"methodConfig": [
   206  				{
   207  					"name": [
   208  						{
   209  							"service": "all"
   210  						}
   211  					],
   212  					"timeout": "1s"
   213  				}
   214  			]
   215  		}
   216  	},
   217  	{
   218  		"percentage": 0,
   219  		"serviceConfig": {
   220  			"loadBalancingPolicy": "grpclb",
   221  			"methodConfig": [
   222  				{
   223  					"name": [
   224  						{
   225  							"service": "all"
   226  						}
   227  					],
   228  					"timeout": "1s"
   229  				}
   230  			]
   231  		}
   232  	},
   233  	{
   234  		"clientHostName": [
   235  			"localhost"
   236  		],
   237  		"serviceConfig": {
   238  			"loadBalancingPolicy": "grpclb",
   239  			"methodConfig": [
   240  				{
   241  					"name": [
   242  						{
   243  							"service": "all"
   244  						}
   245  					],
   246  					"timeout": "1s"
   247  				}
   248  			]
   249  		}
   250  	},
   251  	{
   252  		"clientLanguage": [
   253  			"GO"
   254  		],
   255  		"percentage": 100,
   256  		"serviceConfig": {
   257  			"loadBalancingPolicy": "round_robin",
   258  			"methodConfig": [
   259  				{
   260  					"name": [
   261  						{
   262  							"service": "foo"
   263  						}
   264  					],
   265  					"waitForReady": true,
   266  					"timeout": "1s"
   267  				},
   268  				{
   269  					"name": [
   270  						{
   271  							"service": "bar"
   272  						}
   273  					],
   274  					"waitForReady": false
   275  				}
   276  			]
   277  		}
   278  	},
   279  	{
   280  		"serviceConfig": {
   281  			"loadBalancingPolicy": "round_robin",
   282  			"methodConfig": [
   283  				{
   284  					"name": [
   285  						{
   286  							"service": "foo",
   287  							"method": "bar"
   288  						}
   289  					],
   290  					"waitForReady": true
   291  				}
   292  			]
   293  		}
   294  	}
   295  ]`
   296  
   297  // This is the matched portion of the above TXT record entry.
   298  const scJSON = `
   299  {
   300  	"loadBalancingPolicy": "round_robin",
   301  	"methodConfig": [
   302  		{
   303  			"name": [
   304  				{
   305  					"service": "foo"
   306  				}
   307  			],
   308  			"waitForReady": true,
   309  			"timeout": "1s"
   310  		},
   311  		{
   312  			"name": [
   313  				{
   314  					"service": "bar"
   315  				}
   316  			],
   317  			"waitForReady": false
   318  		}
   319  	]
   320  }`
   321  
   322  // This service config contains three entries, but none of the match the DNS
   323  // resolver's canarying rules and hence the resulting service config pushed by
   324  // the DNS resolver will be an empty one.
   325  const txtRecordNonMatching = `
   326  [
   327  	{
   328  		"clientLanguage": [
   329  			"CPP",
   330  			"JAVA"
   331  		],
   332  		"serviceConfig": {
   333  			"loadBalancingPolicy": "grpclb",
   334  			"methodConfig": [
   335  				{
   336  					"name": [
   337  						{
   338  							"service": "all"
   339  						}
   340  					],
   341  					"timeout": "1s"
   342  				}
   343  			]
   344  		}
   345  	},
   346  	{
   347  		"percentage": 0,
   348  		"serviceConfig": {
   349  			"loadBalancingPolicy": "grpclb",
   350  			"methodConfig": [
   351  				{
   352  					"name": [
   353  						{
   354  							"service": "all"
   355  						}
   356  					],
   357  					"timeout": "1s"
   358  				}
   359  			]
   360  		}
   361  	},
   362  	{
   363  		"clientHostName": [
   364  			"localhost"
   365  		],
   366  		"serviceConfig": {
   367  			"loadBalancingPolicy": "grpclb",
   368  			"methodConfig": [
   369  				{
   370  					"name": [
   371  						{
   372  							"service": "all"
   373  						}
   374  					],
   375  					"timeout": "1s"
   376  				}
   377  			]
   378  		}
   379  	}
   380  ]`
   381  
   382  // Tests the scenario where a name resolves to a list of addresses, possibly
   383  // some grpclb addresses as well, and a service config. The test verifies that
   384  // the expected update is pushed to the channel.
   385  func (s) TestDNSResolver_Basic(t *testing.T) {
   386  	tests := []struct {
   387  		name              string
   388  		target            string
   389  		hostLookupTable   map[string][]string
   390  		srvLookupTable    map[string][]*net.SRV
   391  		txtLookupTable    map[string][]string
   392  		wantAddrs         []resolver.Address
   393  		wantBalancerAddrs []resolver.Address
   394  		wantSC            string
   395  	}{
   396  		{
   397  			name:   "default_port",
   398  			target: "foo.bar.com",
   399  			hostLookupTable: map[string][]string{
   400  				"foo.bar.com": {"1.2.3.4", "5.6.7.8"},
   401  			},
   402  			txtLookupTable: map[string][]string{
   403  				"_grpc_config.foo.bar.com": txtRecordServiceConfig(txtRecordGood),
   404  			},
   405  			wantAddrs:         []resolver.Address{{Addr: "1.2.3.4" + colonDefaultPort}, {Addr: "5.6.7.8" + colonDefaultPort}},
   406  			wantBalancerAddrs: nil,
   407  			wantSC:            scJSON,
   408  		},
   409  		{
   410  			name:   "specified_port",
   411  			target: "foo.bar.com:1234",
   412  			hostLookupTable: map[string][]string{
   413  				"foo.bar.com": {"1.2.3.4", "5.6.7.8"},
   414  			},
   415  			txtLookupTable: map[string][]string{
   416  				"_grpc_config.foo.bar.com": txtRecordServiceConfig(txtRecordGood),
   417  			},
   418  			wantAddrs:         []resolver.Address{{Addr: "1.2.3.4:1234"}, {Addr: "5.6.7.8:1234"}},
   419  			wantBalancerAddrs: nil,
   420  			wantSC:            scJSON,
   421  		},
   422  		{
   423  			name:   "ipv4_with_SRV_and_single_grpclb_address",
   424  			target: "srv.ipv4.single.fake",
   425  			hostLookupTable: map[string][]string{
   426  				"srv.ipv4.single.fake": {"2.4.6.8"},
   427  				"ipv4.single.fake":     {"1.2.3.4"},
   428  			},
   429  			srvLookupTable: map[string][]*net.SRV{
   430  				"_grpclb._tcp.srv.ipv4.single.fake": {&net.SRV{Target: "ipv4.single.fake", Port: 1234}},
   431  			},
   432  			txtLookupTable: map[string][]string{
   433  				"_grpc_config.srv.ipv4.single.fake": txtRecordServiceConfig(txtRecordGood),
   434  			},
   435  			wantAddrs:         []resolver.Address{{Addr: "2.4.6.8" + colonDefaultPort}},
   436  			wantBalancerAddrs: []resolver.Address{{Addr: "1.2.3.4:1234", ServerName: "ipv4.single.fake"}},
   437  			wantSC:            scJSON,
   438  		},
   439  		{
   440  			name:   "ipv4_with_SRV_and_multiple_grpclb_address",
   441  			target: "srv.ipv4.multi.fake",
   442  			hostLookupTable: map[string][]string{
   443  				"ipv4.multi.fake": {"1.2.3.4", "5.6.7.8", "9.10.11.12"},
   444  			},
   445  			srvLookupTable: map[string][]*net.SRV{
   446  				"_grpclb._tcp.srv.ipv4.multi.fake": {&net.SRV{Target: "ipv4.multi.fake", Port: 1234}},
   447  			},
   448  			txtLookupTable: map[string][]string{
   449  				"_grpc_config.srv.ipv4.multi.fake": txtRecordServiceConfig(txtRecordGood),
   450  			},
   451  			wantAddrs: nil,
   452  			wantBalancerAddrs: []resolver.Address{
   453  				{Addr: "1.2.3.4:1234", ServerName: "ipv4.multi.fake"},
   454  				{Addr: "5.6.7.8:1234", ServerName: "ipv4.multi.fake"},
   455  				{Addr: "9.10.11.12:1234", ServerName: "ipv4.multi.fake"},
   456  			},
   457  			wantSC: scJSON,
   458  		},
   459  		{
   460  			name:   "ipv6_with_SRV_and_single_grpclb_address",
   461  			target: "srv.ipv6.single.fake",
   462  			hostLookupTable: map[string][]string{
   463  				"srv.ipv6.single.fake": nil,
   464  				"ipv6.single.fake":     {"2607:f8b0:400a:801::1001"},
   465  			},
   466  			srvLookupTable: map[string][]*net.SRV{
   467  				"_grpclb._tcp.srv.ipv6.single.fake": {&net.SRV{Target: "ipv6.single.fake", Port: 1234}},
   468  			},
   469  			txtLookupTable: map[string][]string{
   470  				"_grpc_config.srv.ipv6.single.fake": txtRecordServiceConfig(txtRecordNonMatching),
   471  			},
   472  			wantAddrs:         nil,
   473  			wantBalancerAddrs: []resolver.Address{{Addr: "[2607:f8b0:400a:801::1001]:1234", ServerName: "ipv6.single.fake"}},
   474  			wantSC:            "{}",
   475  		},
   476  		{
   477  			name:   "ipv6_with_SRV_and_multiple_grpclb_address",
   478  			target: "srv.ipv6.multi.fake",
   479  			hostLookupTable: map[string][]string{
   480  				"srv.ipv6.multi.fake": nil,
   481  				"ipv6.multi.fake":     {"2607:f8b0:400a:801::1001", "2607:f8b0:400a:801::1002", "2607:f8b0:400a:801::1003"},
   482  			},
   483  			srvLookupTable: map[string][]*net.SRV{
   484  				"_grpclb._tcp.srv.ipv6.multi.fake": {&net.SRV{Target: "ipv6.multi.fake", Port: 1234}},
   485  			},
   486  			txtLookupTable: map[string][]string{
   487  				"_grpc_config.srv.ipv6.multi.fake": txtRecordServiceConfig(txtRecordNonMatching),
   488  			},
   489  			wantAddrs: nil,
   490  			wantBalancerAddrs: []resolver.Address{
   491  				{Addr: "[2607:f8b0:400a:801::1001]:1234", ServerName: "ipv6.multi.fake"},
   492  				{Addr: "[2607:f8b0:400a:801::1002]:1234", ServerName: "ipv6.multi.fake"},
   493  				{Addr: "[2607:f8b0:400a:801::1003]:1234", ServerName: "ipv6.multi.fake"},
   494  			},
   495  			wantSC: "{}",
   496  		},
   497  	}
   498  
   499  	for _, test := range tests {
   500  		t.Run(test.name, func(t *testing.T) {
   501  			overrideTimeAfterFunc(t, 2*defaultTestTimeout)
   502  			overrideNetResolver(t, &testNetResolver{
   503  				hostLookupTable: test.hostLookupTable,
   504  				srvLookupTable:  test.srvLookupTable,
   505  				txtLookupTable:  test.txtLookupTable,
   506  			})
   507  			enableSRVLookups(t)
   508  			_, stateCh, _ := buildResolverWithTestClientConn(t, test.target)
   509  
   510  			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   511  			defer cancel()
   512  			verifyUpdateFromResolver(ctx, t, stateCh, test.wantAddrs, test.wantBalancerAddrs, test.wantSC)
   513  		})
   514  	}
   515  }
   516  
   517  // Tests the case where the channel returns an error for the update pushed by
   518  // the DNS resolver. Verifies that the DNS resolver backs off before trying to
   519  // resolve. Once the channel returns a nil error, the test verifies that the DNS
   520  // resolver does not backoff anymore.
   521  func (s) TestDNSResolver_ExponentialBackoff(t *testing.T) {
   522  	tests := []struct {
   523  		name            string
   524  		target          string
   525  		hostLookupTable map[string][]string
   526  		txtLookupTable  map[string][]string
   527  		wantAddrs       []resolver.Address
   528  		wantSC          string
   529  	}{
   530  		{
   531  			name:            "happy case default port",
   532  			target:          "foo.bar.com",
   533  			hostLookupTable: map[string][]string{"foo.bar.com": {"1.2.3.4", "5.6.7.8"}},
   534  			txtLookupTable: map[string][]string{
   535  				"_grpc_config.foo.bar.com": txtRecordServiceConfig(txtRecordGood),
   536  			},
   537  			wantAddrs: []resolver.Address{{Addr: "1.2.3.4" + colonDefaultPort}, {Addr: "5.6.7.8" + colonDefaultPort}},
   538  			wantSC:    scJSON,
   539  		},
   540  		{
   541  			name:            "happy case specified port",
   542  			target:          "foo.bar.com:1234",
   543  			hostLookupTable: map[string][]string{"foo.bar.com": {"1.2.3.4", "5.6.7.8"}},
   544  			txtLookupTable: map[string][]string{
   545  				"_grpc_config.foo.bar.com": txtRecordServiceConfig(txtRecordGood),
   546  			},
   547  			wantAddrs: []resolver.Address{{Addr: "1.2.3.4:1234"}, {Addr: "5.6.7.8:1234"}},
   548  			wantSC:    scJSON,
   549  		},
   550  		{
   551  			name:   "happy case another default port",
   552  			target: "srv.ipv4.single.fake",
   553  			hostLookupTable: map[string][]string{
   554  				"srv.ipv4.single.fake": {"2.4.6.8"},
   555  				"ipv4.single.fake":     {"1.2.3.4"},
   556  			},
   557  			txtLookupTable: map[string][]string{
   558  				"_grpc_config.srv.ipv4.single.fake": txtRecordServiceConfig(txtRecordGood),
   559  			},
   560  			wantAddrs: []resolver.Address{{Addr: "2.4.6.8" + colonDefaultPort}},
   561  			wantSC:    scJSON,
   562  		},
   563  	}
   564  	for _, test := range tests {
   565  		t.Run(test.name, func(t *testing.T) {
   566  			durChan, timeChan := overrideTimeAfterFuncWithChannel(t)
   567  			overrideNetResolver(t, &testNetResolver{
   568  				hostLookupTable: test.hostLookupTable,
   569  				txtLookupTable:  test.txtLookupTable,
   570  			})
   571  
   572  			// Set the test clientconn to return error back to the resolver when
   573  			// it pushes an update on the channel.
   574  			var returnNilErr atomic.Bool
   575  			updateStateF := func(s resolver.State) error {
   576  				if returnNilErr.Load() {
   577  					return nil
   578  				}
   579  				return balancer.ErrBadResolverState
   580  			}
   581  			tcc := &testutils.ResolverClientConn{Logger: t, UpdateStateF: updateStateF}
   582  
   583  			b := resolver.Get("dns")
   584  			if b == nil {
   585  				t.Fatalf("Resolver for dns:/// scheme not registered")
   586  			}
   587  			r, err := b.Build(resolver.Target{URL: *testutils.MustParseURL(fmt.Sprintf("dns:///%s", test.target))}, tcc, resolver.BuildOptions{})
   588  			if err != nil {
   589  				t.Fatalf("Failed to build DNS resolver for target %q: %v\n", test.target, err)
   590  			}
   591  			defer r.Close()
   592  
   593  			// Expect the DNS resolver to backoff and attempt to re-resolve.
   594  			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   595  			defer cancel()
   596  			const retries = 10
   597  			var prevDur time.Duration
   598  			for i := 0; i < retries; i++ {
   599  				select {
   600  				case <-ctx.Done():
   601  					t.Fatalf("(Iteration: %d): Timeout when waiting for DNS resolver to backoff", i)
   602  				case dur := <-durChan:
   603  					if dur <= prevDur {
   604  						t.Fatalf("(Iteration: %d): Unexpected decrease in amount of time to backoff", i)
   605  					}
   606  				}
   607  
   608  				// Unblock the DNS resolver's backoff by pushing the current time.
   609  				timeChan <- time.Now()
   610  			}
   611  
   612  			// Update resolver.ClientConn to not return an error anymore.
   613  			returnNilErr.Store(true)
   614  
   615  			// Unblock the DNS resolver's backoff, if ongoing, while we set the
   616  			// test clientConn to not return an error anymore.
   617  			select {
   618  			case timeChan <- time.Now():
   619  			default:
   620  			}
   621  
   622  			// Verify that the DNS resolver does not backoff anymore.
   623  			sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
   624  			defer sCancel()
   625  			select {
   626  			case <-durChan:
   627  				t.Fatal("Unexpected DNS resolver backoff")
   628  			case <-sCtx.Done():
   629  			}
   630  		})
   631  	}
   632  }
   633  
   634  // Tests the case where the DNS resolver is asked to re-resolve by invoking the
   635  // ResolveNow method.
   636  func (s) TestDNSResolver_ResolveNow(t *testing.T) {
   637  	const target = "foo.bar.com"
   638  
   639  	overrideResolutionInterval(t, 0)
   640  	overrideTimeAfterFunc(t, 0)
   641  	tr := &testNetResolver{
   642  		hostLookupTable: map[string][]string{
   643  			"foo.bar.com": {"1.2.3.4", "5.6.7.8"},
   644  		},
   645  		txtLookupTable: map[string][]string{
   646  			"_grpc_config.foo.bar.com": txtRecordServiceConfig(txtRecordGood),
   647  		},
   648  	}
   649  	overrideNetResolver(t, tr)
   650  
   651  	r, stateCh, _ := buildResolverWithTestClientConn(t, target)
   652  
   653  	// Verify that the first update pushed by the resolver matches expectations.
   654  	wantAddrs := []resolver.Address{{Addr: "1.2.3.4" + colonDefaultPort}, {Addr: "5.6.7.8" + colonDefaultPort}}
   655  	wantSC := scJSON
   656  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   657  	defer cancel()
   658  	verifyUpdateFromResolver(ctx, t, stateCh, wantAddrs, nil, wantSC)
   659  
   660  	// Update state in the fake net.Resolver to return only one address and a
   661  	// new service config.
   662  	tr.UpdateHostLookupTable(map[string][]string{target: {"1.2.3.4"}})
   663  	tr.UpdateTXTLookupTable(map[string][]string{
   664  		"_grpc_config.foo.bar.com": txtRecordServiceConfig(`[{"serviceConfig":{"loadBalancingPolicy": "grpclb"}}]`),
   665  	})
   666  
   667  	// Ask the resolver to re-resolve and verify that the new update matches
   668  	// expectations.
   669  	r.ResolveNow(resolver.ResolveNowOptions{})
   670  	wantAddrs = []resolver.Address{{Addr: "1.2.3.4" + colonDefaultPort}}
   671  	wantSC = `{"loadBalancingPolicy": "grpclb"}`
   672  	verifyUpdateFromResolver(ctx, t, stateCh, wantAddrs, nil, wantSC)
   673  
   674  	// Update state in the fake resolver to return no addresses and the same
   675  	// service config as before.
   676  	tr.UpdateHostLookupTable(map[string][]string{target: nil})
   677  
   678  	// Ask the resolver to re-resolve and verify that the new update matches
   679  	// expectations.
   680  	r.ResolveNow(resolver.ResolveNowOptions{})
   681  	verifyUpdateFromResolver(ctx, t, stateCh, nil, nil, wantSC)
   682  }
   683  
   684  // Tests the case where the given name is an IP address and verifies that the
   685  // update pushed by the DNS resolver meets expectations.
   686  func (s) TestIPResolver(t *testing.T) {
   687  	tests := []struct {
   688  		name     string
   689  		target   string
   690  		wantAddr []resolver.Address
   691  	}{
   692  		{
   693  			name:     "localhost ipv4 default port",
   694  			target:   "127.0.0.1",
   695  			wantAddr: []resolver.Address{{Addr: "127.0.0.1:443"}},
   696  		},
   697  		{
   698  			name:     "localhost ipv4 non-default port",
   699  			target:   "127.0.0.1:12345",
   700  			wantAddr: []resolver.Address{{Addr: "127.0.0.1:12345"}},
   701  		},
   702  		{
   703  			name:     "localhost ipv6 default port no brackets",
   704  			target:   "::1",
   705  			wantAddr: []resolver.Address{{Addr: "[::1]:443"}},
   706  		},
   707  		{
   708  			name:     "localhost ipv6 default port with brackets",
   709  			target:   "[::1]",
   710  			wantAddr: []resolver.Address{{Addr: "[::1]:443"}},
   711  		},
   712  		{
   713  			name:     "localhost ipv6 non-default port",
   714  			target:   "[::1]:12345",
   715  			wantAddr: []resolver.Address{{Addr: "[::1]:12345"}},
   716  		},
   717  		{
   718  			name:     "ipv6 default port no brackets",
   719  			target:   "2001:db8:85a3::8a2e:370:7334",
   720  			wantAddr: []resolver.Address{{Addr: "[2001:db8:85a3::8a2e:370:7334]:443"}},
   721  		},
   722  		{
   723  			name:     "ipv6 default port with brackets",
   724  			target:   "[2001:db8:85a3::8a2e:370:7334]",
   725  			wantAddr: []resolver.Address{{Addr: "[2001:db8:85a3::8a2e:370:7334]:443"}},
   726  		},
   727  		{
   728  			name:     "ipv6 non-default port with brackets",
   729  			target:   "[2001:db8:85a3::8a2e:370:7334]:12345",
   730  			wantAddr: []resolver.Address{{Addr: "[2001:db8:85a3::8a2e:370:7334]:12345"}},
   731  		},
   732  		{
   733  			name:     "abbreviated ipv6 address",
   734  			target:   "[2001:db8::1]:http",
   735  			wantAddr: []resolver.Address{{Addr: "[2001:db8::1]:http"}},
   736  		},
   737  		// TODO(yuxuanli): zone support?
   738  	}
   739  
   740  	for _, test := range tests {
   741  		t.Run(test.name, func(t *testing.T) {
   742  			overrideResolutionInterval(t, 0)
   743  			overrideTimeAfterFunc(t, 2*defaultTestTimeout)
   744  			r, stateCh, _ := buildResolverWithTestClientConn(t, test.target)
   745  
   746  			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   747  			defer cancel()
   748  			verifyUpdateFromResolver(ctx, t, stateCh, test.wantAddr, nil, "")
   749  
   750  			// Attempt to re-resolve should not result in a state update.
   751  			r.ResolveNow(resolver.ResolveNowOptions{})
   752  			sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
   753  			defer sCancel()
   754  			select {
   755  			case <-sCtx.Done():
   756  			case s := <-stateCh:
   757  				t.Fatalf("Unexpected state update from the resolver: %+v", s)
   758  			}
   759  		})
   760  	}
   761  }
   762  
   763  // Tests the DNS resolver builder with different target names.
   764  func (s) TestResolverBuild(t *testing.T) {
   765  	tests := []struct {
   766  		name    string
   767  		target  string
   768  		wantErr string
   769  	}{
   770  		{
   771  			name:   "valid url",
   772  			target: "www.google.com",
   773  		},
   774  		{
   775  			name:   "host port",
   776  			target: "foo.bar:12345",
   777  		},
   778  		{
   779  			name:   "ipv4 address with default port",
   780  			target: "127.0.0.1",
   781  		},
   782  		{
   783  			name:   "ipv6 address without brackets and default port",
   784  			target: "::",
   785  		},
   786  		{
   787  			name:   "ipv4 address with non-default port",
   788  			target: "127.0.0.1:12345",
   789  		},
   790  		{
   791  			name:   "localhost ipv6 with brackets",
   792  			target: "[::1]:80",
   793  		},
   794  		{
   795  			name:   "ipv6 address with brackets",
   796  			target: "[2001:db8:a0b:12f0::1]:21",
   797  		},
   798  		{
   799  			name:   "empty host with port",
   800  			target: ":80",
   801  		},
   802  		{
   803  			name:   "ipv6 address with zone",
   804  			target: "[fe80::1%25lo0]:80",
   805  		},
   806  		{
   807  			name:   "url with port",
   808  			target: "golang.org:http",
   809  		},
   810  		{
   811  			name:   "ipv6 address with non integer port",
   812  			target: "[2001:db8::1]:http",
   813  		},
   814  		{
   815  			name:    "address ends with colon",
   816  			target:  "[2001:db8::1]:",
   817  			wantErr: dnsinternal.ErrEndsWithColon.Error(),
   818  		},
   819  		{
   820  			name:    "address contains only a colon",
   821  			target:  ":",
   822  			wantErr: dnsinternal.ErrEndsWithColon.Error(),
   823  		},
   824  		{
   825  			name:    "empty address",
   826  			target:  "",
   827  			wantErr: dnsinternal.ErrMissingAddr.Error(),
   828  		},
   829  		{
   830  			name:    "invalid address",
   831  			target:  "[2001:db8:a0b:12f0::1",
   832  			wantErr: "invalid target address",
   833  		},
   834  	}
   835  
   836  	for _, test := range tests {
   837  		t.Run(test.name, func(t *testing.T) {
   838  			overrideTimeAfterFunc(t, 2*defaultTestTimeout)
   839  
   840  			b := resolver.Get("dns")
   841  			if b == nil {
   842  				t.Fatalf("Resolver for dns:/// scheme not registered")
   843  			}
   844  
   845  			tcc := &testutils.ResolverClientConn{Logger: t}
   846  			r, err := b.Build(resolver.Target{URL: *testutils.MustParseURL(fmt.Sprintf("dns:///%s", test.target))}, tcc, resolver.BuildOptions{})
   847  			if err != nil {
   848  				if test.wantErr == "" {
   849  					t.Fatalf("DNS resolver build for target %q failed with error: %v", test.target, err)
   850  				}
   851  				if !strings.Contains(err.Error(), test.wantErr) {
   852  					t.Fatalf("DNS resolver build for target %q failed with error: %v, wantErr: %s", test.target, err, test.wantErr)
   853  				}
   854  				return
   855  			}
   856  			if err == nil && test.wantErr != "" {
   857  				t.Fatalf("DNS resolver build for target %q succeeded when expected to fail with error: %s", test.target, test.wantErr)
   858  			}
   859  			r.Close()
   860  		})
   861  	}
   862  }
   863  
   864  // Tests scenarios where fetching of service config is enabled or disabled, and
   865  // verifies that the expected update is pushed by the DNS resolver.
   866  func (s) TestDisableServiceConfig(t *testing.T) {
   867  	tests := []struct {
   868  		name                 string
   869  		target               string
   870  		hostLookupTable      map[string][]string
   871  		txtLookupTable       map[string][]string
   872  		disableServiceConfig bool
   873  		wantAddrs            []resolver.Address
   874  		wantSC               string
   875  	}{
   876  		{
   877  			name:            "false",
   878  			target:          "foo.bar.com",
   879  			hostLookupTable: map[string][]string{"foo.bar.com": {"1.2.3.4", "5.6.7.8"}},
   880  			txtLookupTable: map[string][]string{
   881  				"_grpc_config.foo.bar.com": txtRecordServiceConfig(txtRecordGood),
   882  			},
   883  			disableServiceConfig: false,
   884  			wantAddrs:            []resolver.Address{{Addr: "1.2.3.4" + colonDefaultPort}, {Addr: "5.6.7.8" + colonDefaultPort}},
   885  			wantSC:               scJSON,
   886  		},
   887  		{
   888  			name:            "true",
   889  			target:          "foo.bar.com",
   890  			hostLookupTable: map[string][]string{"foo.bar.com": {"1.2.3.4", "5.6.7.8"}},
   891  			txtLookupTable: map[string][]string{
   892  				"_grpc_config.foo.bar.com": txtRecordServiceConfig(txtRecordGood),
   893  			},
   894  			disableServiceConfig: true,
   895  			wantAddrs:            []resolver.Address{{Addr: "1.2.3.4" + colonDefaultPort}, {Addr: "5.6.7.8" + colonDefaultPort}},
   896  			wantSC:               "{}",
   897  		},
   898  	}
   899  
   900  	for _, test := range tests {
   901  		t.Run(test.name, func(t *testing.T) {
   902  			overrideTimeAfterFunc(t, 2*defaultTestTimeout)
   903  			overrideNetResolver(t, &testNetResolver{
   904  				hostLookupTable: test.hostLookupTable,
   905  				txtLookupTable:  test.txtLookupTable,
   906  			})
   907  
   908  			b := resolver.Get("dns")
   909  			if b == nil {
   910  				t.Fatalf("Resolver for dns:/// scheme not registered")
   911  			}
   912  
   913  			stateCh := make(chan resolver.State, 1)
   914  			updateStateF := func(s resolver.State) error {
   915  				stateCh <- s
   916  				return nil
   917  			}
   918  			tcc := &testutils.ResolverClientConn{Logger: t, UpdateStateF: updateStateF}
   919  			r, err := b.Build(resolver.Target{URL: *testutils.MustParseURL(fmt.Sprintf("dns:///%s", test.target))}, tcc, resolver.BuildOptions{DisableServiceConfig: test.disableServiceConfig})
   920  			if err != nil {
   921  				t.Fatalf("Failed to build DNS resolver for target %q: %v\n", test.target, err)
   922  			}
   923  			defer r.Close()
   924  
   925  			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   926  			defer cancel()
   927  			verifyUpdateFromResolver(ctx, t, stateCh, test.wantAddrs, nil, test.wantSC)
   928  		})
   929  	}
   930  }
   931  
   932  // Tests the case where a TXT lookup is expected to return an error. Verifies
   933  // that errors are ignored with the corresponding env var is set.
   934  func (s) TestTXTError(t *testing.T) {
   935  	for _, ignore := range []bool{false, true} {
   936  		t.Run(fmt.Sprintf("%v", ignore), func(t *testing.T) {
   937  			overrideTimeAfterFunc(t, 2*defaultTestTimeout)
   938  			overrideNetResolver(t, &testNetResolver{hostLookupTable: map[string][]string{"ipv4.single.fake": {"1.2.3.4"}}})
   939  
   940  			origTXTIgnore := envconfig.TXTErrIgnore
   941  			envconfig.TXTErrIgnore = ignore
   942  			defer func() { envconfig.TXTErrIgnore = origTXTIgnore }()
   943  
   944  			// There is no entry for "ipv4.single.fake" in the txtLookupTbl
   945  			// maintained by the fake net.Resolver. So, a TXT lookup for this
   946  			// name will return an error.
   947  			_, stateCh, _ := buildResolverWithTestClientConn(t, "ipv4.single.fake")
   948  
   949  			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   950  			defer cancel()
   951  			var state resolver.State
   952  			select {
   953  			case <-ctx.Done():
   954  				t.Fatal("Timeout when waiting for a state update from the resolver")
   955  			case state = <-stateCh:
   956  			}
   957  
   958  			if ignore {
   959  				if state.ServiceConfig != nil {
   960  					t.Fatalf("Received non-nil service config: %+v; want nil", state.ServiceConfig)
   961  				}
   962  			} else {
   963  				if state.ServiceConfig == nil || state.ServiceConfig.Err == nil {
   964  					t.Fatalf("Received service config %+v; want non-nil error", state.ServiceConfig)
   965  				}
   966  			}
   967  		})
   968  	}
   969  }
   970  
   971  // Tests different cases for a user's dial target that specifies a non-empty
   972  // authority (or Host field of the URL).
   973  func (s) TestCustomAuthority(t *testing.T) {
   974  	tests := []struct {
   975  		name          string
   976  		authority     string
   977  		wantAuthority string
   978  		wantBuildErr  bool
   979  	}{
   980  		{
   981  			name:          "authority with default DNS port",
   982  			authority:     "4.3.2.1:53",
   983  			wantAuthority: "4.3.2.1:53",
   984  		},
   985  		{
   986  			name:          "authority with non-default DNS port",
   987  			authority:     "4.3.2.1:123",
   988  			wantAuthority: "4.3.2.1:123",
   989  		},
   990  		{
   991  			name:          "authority with no port",
   992  			authority:     "4.3.2.1",
   993  			wantAuthority: "4.3.2.1:53",
   994  		},
   995  		{
   996  			name:          "ipv6 authority with no port",
   997  			authority:     "::1",
   998  			wantAuthority: "[::1]:53",
   999  		},
  1000  		{
  1001  			name:          "ipv6 authority with brackets and no port",
  1002  			authority:     "[::1]",
  1003  			wantAuthority: "[::1]:53",
  1004  		},
  1005  		{
  1006  			name:          "ipv6 authority with brackers and non-default DNS port",
  1007  			authority:     "[::1]:123",
  1008  			wantAuthority: "[::1]:123",
  1009  		},
  1010  		{
  1011  			name:          "host name with no port",
  1012  			authority:     "dnsserver.com",
  1013  			wantAuthority: "dnsserver.com:53",
  1014  		},
  1015  		{
  1016  			name:          "no host port and non-default port",
  1017  			authority:     ":123",
  1018  			wantAuthority: "localhost:123",
  1019  		},
  1020  		{
  1021  			name:          "only colon",
  1022  			authority:     ":",
  1023  			wantAuthority: "",
  1024  			wantBuildErr:  true,
  1025  		},
  1026  		{
  1027  			name:          "ipv6 name ending in colon",
  1028  			authority:     "[::1]:",
  1029  			wantAuthority: "",
  1030  			wantBuildErr:  true,
  1031  		},
  1032  		{
  1033  			name:          "host name ending in colon",
  1034  			authority:     "dnsserver.com:",
  1035  			wantAuthority: "",
  1036  			wantBuildErr:  true,
  1037  		},
  1038  	}
  1039  
  1040  	for _, test := range tests {
  1041  		t.Run(test.name, func(t *testing.T) {
  1042  			overrideTimeAfterFunc(t, 2*defaultTestTimeout)
  1043  
  1044  			// Override the address dialer to verify the authority being passed.
  1045  			origAddressDialer := dnsinternal.AddressDialer
  1046  			errChan := make(chan error, 1)
  1047  			dnsinternal.AddressDialer = func(authority string) func(ctx context.Context, network, address string) (net.Conn, error) {
  1048  				if authority != test.wantAuthority {
  1049  					errChan <- fmt.Errorf("wrong custom authority passed to resolver. target: %s got authority: %s want authority: %s", test.authority, authority, test.wantAuthority)
  1050  				} else {
  1051  					errChan <- nil
  1052  				}
  1053  				return func(ctx context.Context, network, address string) (net.Conn, error) {
  1054  					return nil, errors.New("no need to dial")
  1055  				}
  1056  			}
  1057  			defer func() { dnsinternal.AddressDialer = origAddressDialer }()
  1058  
  1059  			b := resolver.Get("dns")
  1060  			if b == nil {
  1061  				t.Fatalf("Resolver for dns:/// scheme not registered")
  1062  			}
  1063  
  1064  			tcc := &testutils.ResolverClientConn{Logger: t}
  1065  			endpoint := "foo.bar.com"
  1066  			target := resolver.Target{URL: *testutils.MustParseURL(fmt.Sprintf("dns://%s/%s", test.authority, endpoint))}
  1067  			r, err := b.Build(target, tcc, resolver.BuildOptions{})
  1068  			if (err != nil) != test.wantBuildErr {
  1069  				t.Fatalf("DNS resolver build for target %+v returned error %v: wantErr: %v\n", target, err, test.wantBuildErr)
  1070  			}
  1071  			if err != nil {
  1072  				return
  1073  			}
  1074  			defer r.Close()
  1075  
  1076  			if err := <-errChan; err != nil {
  1077  				t.Fatal(err)
  1078  			}
  1079  		})
  1080  	}
  1081  }
  1082  
  1083  // TestRateLimitedResolve exercises the rate limit enforced on re-resolution
  1084  // requests. It sets the re-resolution rate to a small value and repeatedly
  1085  // calls ResolveNow() and ensures only the expected number of resolution
  1086  // requests are made.
  1087  func (s) TestRateLimitedResolve(t *testing.T) {
  1088  	const target = "foo.bar.com"
  1089  	_, timeChan := overrideTimeAfterFuncWithChannel(t)
  1090  	tr := &testNetResolver{
  1091  		lookupHostCh:    testutils.NewChannel(),
  1092  		hostLookupTable: map[string][]string{target: {"1.2.3.4", "5.6.7.8"}},
  1093  	}
  1094  	overrideNetResolver(t, tr)
  1095  
  1096  	r, stateCh, _ := buildResolverWithTestClientConn(t, target)
  1097  
  1098  	// Wait for the first resolution request to be done. This happens as part
  1099  	// of the first iteration of the for loop in watcher().
  1100  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
  1101  	defer cancel()
  1102  	if _, err := tr.lookupHostCh.Receive(ctx); err != nil {
  1103  		t.Fatalf("Timed out waiting for lookup() call.")
  1104  	}
  1105  
  1106  	// Call Resolve Now 100 times, shouldn't continue onto next iteration of
  1107  	// watcher, thus shouldn't lookup again.
  1108  	for i := 0; i <= 100; i++ {
  1109  		r.ResolveNow(resolver.ResolveNowOptions{})
  1110  	}
  1111  
  1112  	continueCtx, continueCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout)
  1113  	defer continueCancel()
  1114  	if _, err := tr.lookupHostCh.Receive(continueCtx); err == nil {
  1115  		t.Fatalf("Should not have looked up again as DNS Min Res Rate timer has not gone off.")
  1116  	}
  1117  
  1118  	// Make the DNSMinResRate timer fire immediately, by sending the current
  1119  	// time on it. This will unblock the resolver which is currently blocked on
  1120  	// the DNS Min Res Rate timer going off, which will allow it to continue to
  1121  	// the next iteration of the watcher loop.
  1122  	select {
  1123  	case timeChan <- time.Now():
  1124  	case <-ctx.Done():
  1125  		t.Fatal("Timed out waiting for the DNS resolver to block on DNS Min Res Rate to elapse")
  1126  	}
  1127  
  1128  	// Now that DNS Min Res Rate timer has gone off, it should lookup again.
  1129  	if _, err := tr.lookupHostCh.Receive(ctx); err != nil {
  1130  		t.Fatalf("Timed out waiting for lookup() call.")
  1131  	}
  1132  
  1133  	// Resolve Now 1000 more times, shouldn't lookup again as DNS Min Res Rate
  1134  	// timer has not gone off.
  1135  	for i := 0; i < 1000; i++ {
  1136  		r.ResolveNow(resolver.ResolveNowOptions{})
  1137  	}
  1138  	continueCtx, continueCancel = context.WithTimeout(context.Background(), defaultTestShortTimeout)
  1139  	defer continueCancel()
  1140  	if _, err := tr.lookupHostCh.Receive(continueCtx); err == nil {
  1141  		t.Fatalf("Should not have looked up again as DNS Min Res Rate timer has not gone off.")
  1142  	}
  1143  
  1144  	// Make the DNSMinResRate timer fire immediately again.
  1145  	select {
  1146  	case timeChan <- time.Now():
  1147  	case <-ctx.Done():
  1148  		t.Fatal("Timed out waiting for the DNS resolver to block on DNS Min Res Rate to elapse")
  1149  	}
  1150  
  1151  	// Now that DNS Min Res Rate timer has gone off, it should lookup again.
  1152  	if _, err := tr.lookupHostCh.Receive(ctx); err != nil {
  1153  		t.Fatalf("Timed out waiting for lookup() call.")
  1154  	}
  1155  
  1156  	wantAddrs := []resolver.Address{{Addr: "1.2.3.4" + colonDefaultPort}, {Addr: "5.6.7.8" + colonDefaultPort}}
  1157  	var state resolver.State
  1158  	select {
  1159  	case <-ctx.Done():
  1160  		t.Fatal("Timeout when waiting for a state update from the resolver")
  1161  	case state = <-stateCh:
  1162  	}
  1163  	if !cmp.Equal(state.Addresses, wantAddrs, cmpopts.EquateEmpty()) {
  1164  		t.Fatalf("Got addresses: %+v, want: %+v", state.Addresses, wantAddrs)
  1165  	}
  1166  }
  1167  
  1168  // Test verifies that when the DNS resolver gets an error from the underlying
  1169  // net.Resolver, it reports the error to the channel and backs off and retries.
  1170  func (s) TestReportError(t *testing.T) {
  1171  	durChan, timeChan := overrideTimeAfterFuncWithChannel(t)
  1172  	overrideNetResolver(t, &testNetResolver{})
  1173  
  1174  	const target = "notfoundaddress"
  1175  	_, _, errorCh := buildResolverWithTestClientConn(t, target)
  1176  
  1177  	// Should receive first error.
  1178  	ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout)
  1179  	defer ctxCancel()
  1180  	select {
  1181  	case <-ctx.Done():
  1182  		t.Fatal("Timeout when waiting for an error from the resolver")
  1183  	case err := <-errorCh:
  1184  		if !strings.Contains(err.Error(), "hostLookup error") {
  1185  			t.Fatalf(`ReportError(err=%v) called; want err contains "hostLookupError"`, err)
  1186  		}
  1187  	}
  1188  
  1189  	// Expect the DNS resolver to backoff and attempt to re-resolve. Every time,
  1190  	// the DNS resolver will receive the same error from the net.Resolver and is
  1191  	// expected to push it to the channel.
  1192  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
  1193  	defer cancel()
  1194  	const retries = 10
  1195  	var prevDur time.Duration
  1196  	for i := 0; i < retries; i++ {
  1197  		select {
  1198  		case <-ctx.Done():
  1199  			t.Fatalf("(Iteration: %d): Timeout when waiting for DNS resolver to backoff", i)
  1200  		case dur := <-durChan:
  1201  			if dur <= prevDur {
  1202  				t.Fatalf("(Iteration: %d): Unexpected decrease in amount of time to backoff", i)
  1203  			}
  1204  		}
  1205  
  1206  		// Unblock the DNS resolver's backoff by pushing the current time.
  1207  		timeChan <- time.Now()
  1208  
  1209  		select {
  1210  		case <-ctx.Done():
  1211  			t.Fatal("Timeout when waiting for an error from the resolver")
  1212  		case err := <-errorCh:
  1213  			if !strings.Contains(err.Error(), "hostLookup error") {
  1214  				t.Fatalf(`ReportError(err=%v) called; want err contains "hostLookupError"`, err)
  1215  			}
  1216  		}
  1217  	}
  1218  }
  1219  
  1220  // Override the default dns.ResolvingTimeout with a test duration.
  1221  func overrideResolveTimeoutDuration(t *testing.T, dur time.Duration) {
  1222  	t.Helper()
  1223  
  1224  	origDur := dns.ResolvingTimeout
  1225  	dnspublic.SetResolvingTimeout(dur)
  1226  
  1227  	t.Cleanup(func() { dnspublic.SetResolvingTimeout(origDur) })
  1228  }
  1229  
  1230  // Test verifies that the DNS resolver gets timeout error when net.Resolver
  1231  // takes too long to resolve a target.
  1232  func (s) TestResolveTimeout(t *testing.T) {
  1233  	// Set DNS resolving timeout duration to 7ms
  1234  	timeoutDur := 7 * time.Millisecond
  1235  	overrideResolveTimeoutDuration(t, timeoutDur)
  1236  
  1237  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
  1238  	defer cancel()
  1239  
  1240  	// We are trying to resolve hostname which takes infinity time to resolve.
  1241  	const target = "infinity"
  1242  
  1243  	// Define a testNetResolver with lookupHostCh, an unbuffered channel,
  1244  	// so we can block the resolver until reaching timeout.
  1245  	tr := &testNetResolver{
  1246  		lookupHostCh:    testutils.NewChannelWithSize(0),
  1247  		hostLookupTable: map[string][]string{target: {"1.2.3.4"}},
  1248  	}
  1249  	overrideNetResolver(t, tr)
  1250  
  1251  	_, _, errCh := buildResolverWithTestClientConn(t, target)
  1252  	select {
  1253  	case <-ctx.Done():
  1254  		t.Fatal("Timeout when waiting for the DNS resolver to timeout")
  1255  	case err := <-errCh:
  1256  		if err == nil || !strings.Contains(err.Error(), "context deadline exceeded") {
  1257  			t.Fatalf(`Expected to see Timeout error; got: %v`, err)
  1258  		}
  1259  	}
  1260  }
  1261  
  1262  // Test verifies that changing [MinResolutionInterval] variable correctly effects
  1263  // the resolution behaviour
  1264  func (s) TestMinResolutionInterval(t *testing.T) {
  1265  	const target = "foo.bar.com"
  1266  
  1267  	overrideResolutionInterval(t, 1*time.Millisecond)
  1268  	tr := &testNetResolver{
  1269  		hostLookupTable: map[string][]string{
  1270  			"foo.bar.com": {"1.2.3.4", "5.6.7.8"},
  1271  		},
  1272  		txtLookupTable: map[string][]string{
  1273  			"_grpc_config.foo.bar.com": txtRecordServiceConfig(txtRecordGood),
  1274  		},
  1275  	}
  1276  	overrideNetResolver(t, tr)
  1277  
  1278  	r, stateCh, _ := buildResolverWithTestClientConn(t, target)
  1279  
  1280  	wantAddrs := []resolver.Address{{Addr: "1.2.3.4" + colonDefaultPort}, {Addr: "5.6.7.8" + colonDefaultPort}}
  1281  	wantSC := scJSON
  1282  
  1283  	for i := 0; i < 5; i++ {
  1284  		// set context timeout slightly higher than the min resolution interval to make sure resolutions
  1285  		// happen successfully
  1286  		ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
  1287  		defer cancel()
  1288  
  1289  		verifyUpdateFromResolver(ctx, t, stateCh, wantAddrs, nil, wantSC)
  1290  		r.ResolveNow(resolver.ResolveNowOptions{})
  1291  	}
  1292  }
  1293  

View as plain text