1
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"
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
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
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
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
88
89
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
112
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
149
150
151
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
188
189
190
191
192
193
194
195
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
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
323
324
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
383
384
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
518
519
520
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
573
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
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
609 timeChan <- time.Now()
610 }
611
612
613 returnNilErr.Store(true)
614
615
616
617 select {
618 case timeChan <- time.Now():
619 default:
620 }
621
622
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
635
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
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
661
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
668
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
675
676 tr.UpdateHostLookupTable(map[string][]string{target: nil})
677
678
679
680 r.ResolveNow(resolver.ResolveNowOptions{})
681 verifyUpdateFromResolver(ctx, t, stateCh, nil, nil, wantSC)
682 }
683
684
685
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
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
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
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
865
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
933
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
945
946
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
972
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
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
1084
1085
1086
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
1099
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
1107
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
1119
1120
1121
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
1129 if _, err := tr.lookupHostCh.Receive(ctx); err != nil {
1130 t.Fatalf("Timed out waiting for lookup() call.")
1131 }
1132
1133
1134
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
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
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
1169
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
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
1190
1191
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
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
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
1231
1232 func (s) TestResolveTimeout(t *testing.T) {
1233
1234 timeoutDur := 7 * time.Millisecond
1235 overrideResolveTimeoutDuration(t, timeoutDur)
1236
1237 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
1238 defer cancel()
1239
1240
1241 const target = "infinity"
1242
1243
1244
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
1263
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
1285
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