google.golang.org/grpc@v1.62.1/internal/resolver/dns/dns_resolver_test.go (about)

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