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 }