mirror of https://github.com/grpc/grpc-go.git
dns: reapply "dns: stop polling for updates; use UpdateState API" (#3228)
This commit is contained in:
parent
895b36ddf2
commit
6f8ce09297
|
@ -32,11 +32,10 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc/backoff"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
internalbackoff "google.golang.org/grpc/internal/backoff"
|
||||
"google.golang.org/grpc/internal/grpcrand"
|
||||
"google.golang.org/grpc/resolver"
|
||||
"google.golang.org/grpc/serviceconfig"
|
||||
)
|
||||
|
||||
// EnableSRVLookups controls whether the DNS resolver attempts to fetch gRPCLB
|
||||
|
@ -49,7 +48,6 @@ func init() {
|
|||
|
||||
const (
|
||||
defaultPort = "443"
|
||||
defaultFreq = time.Minute * 30
|
||||
defaultDNSSvrPort = "53"
|
||||
golang = "GO"
|
||||
// txtPrefix is the prefix string to be prepended to the host name for txt record lookup.
|
||||
|
@ -99,13 +97,10 @@ var customAuthorityResolver = func(authority string) (netResolver, error) {
|
|||
|
||||
// NewBuilder creates a dnsBuilder which is used to factory DNS resolvers.
|
||||
func NewBuilder() resolver.Builder {
|
||||
return &dnsBuilder{minFreq: defaultFreq}
|
||||
return &dnsBuilder{}
|
||||
}
|
||||
|
||||
type dnsBuilder struct {
|
||||
// minimum frequency of polling the DNS server.
|
||||
minFreq time.Duration
|
||||
}
|
||||
type dnsBuilder struct{}
|
||||
|
||||
// Build creates and starts a DNS resolver that watches the name resolution of the target.
|
||||
func (b *dnsBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
|
||||
|
@ -115,33 +110,20 @@ func (b *dnsBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts
|
|||
}
|
||||
|
||||
// IP address.
|
||||
if net.ParseIP(host) != nil {
|
||||
host, _ = formatIP(host)
|
||||
addr := []resolver.Address{{Addr: host + ":" + port}}
|
||||
i := &ipResolver{
|
||||
cc: cc,
|
||||
ip: addr,
|
||||
rn: make(chan struct{}, 1),
|
||||
q: make(chan struct{}),
|
||||
}
|
||||
cc.NewAddress(addr)
|
||||
go i.watcher()
|
||||
return i, nil
|
||||
if ipAddr, ok := formatIP(host); ok {
|
||||
addr := []resolver.Address{{Addr: ipAddr + ":" + port}}
|
||||
cc.UpdateState(resolver.State{Addresses: addr})
|
||||
return deadResolver{}, nil
|
||||
}
|
||||
|
||||
// DNS address (non-IP).
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
bc := backoff.DefaultConfig
|
||||
bc.MaxDelay = b.minFreq
|
||||
d := &dnsResolver{
|
||||
freq: b.minFreq,
|
||||
backoff: internalbackoff.Exponential{Config: bc},
|
||||
host: host,
|
||||
port: port,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
cc: cc,
|
||||
t: time.NewTimer(0),
|
||||
rn: make(chan struct{}, 1),
|
||||
disableServiceConfig: opts.DisableServiceConfig,
|
||||
}
|
||||
|
@ -157,6 +139,7 @@ func (b *dnsBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts
|
|||
|
||||
d.wg.Add(1)
|
||||
go d.watcher()
|
||||
d.ResolveNow(resolver.ResolveNowOptions{})
|
||||
return d, nil
|
||||
}
|
||||
|
||||
|
@ -171,53 +154,23 @@ type netResolver interface {
|
|||
LookupTXT(ctx context.Context, name string) (txts []string, err error)
|
||||
}
|
||||
|
||||
// ipResolver watches for the name resolution update for an IP address.
|
||||
type ipResolver struct {
|
||||
cc resolver.ClientConn
|
||||
ip []resolver.Address
|
||||
// rn channel is used by ResolveNow() to force an immediate resolution of the target.
|
||||
rn chan struct{}
|
||||
q chan struct{}
|
||||
}
|
||||
// deadResolver is a resolver that does nothing.
|
||||
type deadResolver struct{}
|
||||
|
||||
// ResolveNow resend the address it stores, no resolution is needed.
|
||||
func (i *ipResolver) ResolveNow(opt resolver.ResolveNowOptions) {
|
||||
select {
|
||||
case i.rn <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
func (deadResolver) ResolveNow(resolver.ResolveNowOptions) {}
|
||||
|
||||
// Close closes the ipResolver.
|
||||
func (i *ipResolver) Close() {
|
||||
close(i.q)
|
||||
}
|
||||
|
||||
func (i *ipResolver) watcher() {
|
||||
for {
|
||||
select {
|
||||
case <-i.rn:
|
||||
i.cc.NewAddress(i.ip)
|
||||
case <-i.q:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
func (deadResolver) Close() {}
|
||||
|
||||
// dnsResolver watches for the name resolution update for a non-IP target.
|
||||
type dnsResolver struct {
|
||||
freq time.Duration
|
||||
backoff internalbackoff.Exponential
|
||||
retryCount int
|
||||
host string
|
||||
port string
|
||||
resolver netResolver
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
cc resolver.ClientConn
|
||||
host string
|
||||
port string
|
||||
resolver netResolver
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
cc resolver.ClientConn
|
||||
// rn channel is used by ResolveNow() to force an immediate resolution of the target.
|
||||
rn chan struct{}
|
||||
t *time.Timer
|
||||
// wg is used to enforce Close() to return after the watcher() goroutine has finished.
|
||||
// Otherwise, data race will be possible. [Race Example] in dns_resolver_test we
|
||||
// replace the real lookup functions with mocked ones to facilitate testing.
|
||||
|
@ -229,7 +182,7 @@ type dnsResolver struct {
|
|||
}
|
||||
|
||||
// ResolveNow invoke an immediate resolution of the target that this dnsResolver watches.
|
||||
func (d *dnsResolver) ResolveNow(opt resolver.ResolveNowOptions) {
|
||||
func (d *dnsResolver) ResolveNow(resolver.ResolveNowOptions) {
|
||||
select {
|
||||
case d.rn <- struct{}{}:
|
||||
default:
|
||||
|
@ -240,7 +193,6 @@ func (d *dnsResolver) ResolveNow(opt resolver.ResolveNowOptions) {
|
|||
func (d *dnsResolver) Close() {
|
||||
d.cancel()
|
||||
d.wg.Wait()
|
||||
d.t.Stop()
|
||||
}
|
||||
|
||||
func (d *dnsResolver) watcher() {
|
||||
|
@ -249,29 +201,11 @@ func (d *dnsResolver) watcher() {
|
|||
select {
|
||||
case <-d.ctx.Done():
|
||||
return
|
||||
case <-d.t.C:
|
||||
case <-d.rn:
|
||||
if !d.t.Stop() {
|
||||
// Before resetting a timer, it should be stopped to prevent racing with
|
||||
// reads on it's channel.
|
||||
<-d.t.C
|
||||
}
|
||||
}
|
||||
|
||||
result, sc := d.lookup()
|
||||
// Next lookup should happen within an interval defined by d.freq. It may be
|
||||
// more often due to exponential retry on empty address list.
|
||||
if len(result) == 0 {
|
||||
d.retryCount++
|
||||
d.t.Reset(d.backoff.Backoff(d.retryCount))
|
||||
} else {
|
||||
d.retryCount = 0
|
||||
d.t.Reset(d.freq)
|
||||
}
|
||||
if sc != "" { // We get empty string when disabled or the TXT lookup failed.
|
||||
d.cc.NewServiceConfig(sc)
|
||||
}
|
||||
d.cc.NewAddress(result)
|
||||
state := d.lookup()
|
||||
d.cc.UpdateState(*state)
|
||||
|
||||
// Sleep to prevent excessive re-resolutions. Incoming resolution requests
|
||||
// will be queued in d.rn.
|
||||
|
@ -314,11 +248,26 @@ func (d *dnsResolver) lookupSRV() []resolver.Address {
|
|||
return newAddrs
|
||||
}
|
||||
|
||||
func (d *dnsResolver) lookupTXT() string {
|
||||
var filterError = func(err error) error {
|
||||
if dnsErr, ok := err.(*net.DNSError); ok && !dnsErr.IsTimeout && !dnsErr.IsTemporary {
|
||||
// Timeouts and temporary errors should be communicated to gRPC to
|
||||
// attempt another DNS query (with backoff). Other errors should be
|
||||
// suppressed (they may represent the absence of a TXT record).
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *dnsResolver) lookupTXT() *serviceconfig.ParseResult {
|
||||
ss, err := d.resolver.LookupTXT(d.ctx, txtPrefix+d.host)
|
||||
if err != nil {
|
||||
grpclog.Infof("grpc: failed dns TXT record lookup due to %v.\n", err)
|
||||
return ""
|
||||
err = filterError(err)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error from DNS TXT record lookup: %v", err)
|
||||
grpclog.Infoln("grpc:", err)
|
||||
return &serviceconfig.ParseResult{Err: err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
var res string
|
||||
for _, s := range ss {
|
||||
|
@ -327,10 +276,12 @@ func (d *dnsResolver) lookupTXT() string {
|
|||
|
||||
// TXT record must have "grpc_config=" attribute in order to be used as service config.
|
||||
if !strings.HasPrefix(res, txtAttribute) {
|
||||
grpclog.Warningf("grpc: TXT record %v missing %v attribute", res, txtAttribute)
|
||||
return ""
|
||||
grpclog.Warningf("grpc: DNS TXT record %v missing %v attribute", res, txtAttribute)
|
||||
// This is not an error; it is the equivalent of not having a service config.
|
||||
return nil
|
||||
}
|
||||
return strings.TrimPrefix(res, txtAttribute)
|
||||
sc := canaryingSC(strings.TrimPrefix(res, txtAttribute))
|
||||
return d.cc.ParseServiceConfig(sc)
|
||||
}
|
||||
|
||||
func (d *dnsResolver) lookupHost() []resolver.Address {
|
||||
|
@ -352,15 +303,15 @@ func (d *dnsResolver) lookupHost() []resolver.Address {
|
|||
return newAddrs
|
||||
}
|
||||
|
||||
func (d *dnsResolver) lookup() ([]resolver.Address, string) {
|
||||
newAddrs := d.lookupSRV()
|
||||
// Support fallback to non-balancer address.
|
||||
newAddrs = append(newAddrs, d.lookupHost()...)
|
||||
if d.disableServiceConfig {
|
||||
return newAddrs, ""
|
||||
func (d *dnsResolver) lookup() *resolver.State {
|
||||
srv := d.lookupSRV()
|
||||
state := &resolver.State{
|
||||
Addresses: append(d.lookupHost(), srv...),
|
||||
}
|
||||
sc := d.lookupTXT()
|
||||
return newAddrs, canaryingSC(sc)
|
||||
if !d.disableServiceConfig {
|
||||
state.ServiceConfig = d.lookupTXT()
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
// formatIP returns ok = false if addr is not a valid textual representation of an IP address.
|
||||
|
|
|
@ -35,14 +35,11 @@ import (
|
|||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
// Set a valid duration for the re-resolution rate only for tests which are
|
||||
// actually testing that feature.
|
||||
dc := replaceDNSResRate(time.Duration(0))
|
||||
defer dc()
|
||||
|
||||
cleanup := replaceNetFunc(nil)
|
||||
// Set a non-zero duration only for tests which are actually testing that
|
||||
// feature.
|
||||
replaceDNSResRate(time.Duration(0)) // No nead to clean up since we os.Exit
|
||||
replaceNetFunc(nil) // No nead to clean up since we os.Exit
|
||||
code := m.Run()
|
||||
cleanup()
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
|
@ -51,47 +48,43 @@ const (
|
|||
)
|
||||
|
||||
type testClientConn struct {
|
||||
target string
|
||||
m1 sync.Mutex
|
||||
addrs []resolver.Address
|
||||
a int // how many times NewAddress() has been called
|
||||
m2 sync.Mutex
|
||||
sc string
|
||||
s int
|
||||
resolver.ClientConn // For unimplemented functions
|
||||
target string
|
||||
m1 sync.Mutex
|
||||
state resolver.State
|
||||
updateStateCalls int
|
||||
}
|
||||
|
||||
func (t *testClientConn) UpdateState(s resolver.State) {
|
||||
panic("unused")
|
||||
}
|
||||
|
||||
func (t *testClientConn) NewAddress(addresses []resolver.Address) {
|
||||
t.m1.Lock()
|
||||
defer t.m1.Unlock()
|
||||
t.addrs = addresses
|
||||
t.a++
|
||||
t.state = s
|
||||
t.updateStateCalls++
|
||||
}
|
||||
|
||||
func (t *testClientConn) getAddress() ([]resolver.Address, int) {
|
||||
func (t *testClientConn) getState() (resolver.State, int) {
|
||||
t.m1.Lock()
|
||||
defer t.m1.Unlock()
|
||||
return t.addrs, t.a
|
||||
return t.state, t.updateStateCalls
|
||||
}
|
||||
|
||||
func (t *testClientConn) NewServiceConfig(serviceConfig string) {
|
||||
t.m2.Lock()
|
||||
defer t.m2.Unlock()
|
||||
t.sc = serviceConfig
|
||||
t.s++
|
||||
func scFromState(s resolver.State) string {
|
||||
if s.ServiceConfig != nil {
|
||||
if s.ServiceConfig.Err != nil {
|
||||
return ""
|
||||
}
|
||||
return s.ServiceConfig.Config.(unparsedServiceConfig).config
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (t *testClientConn) getSc() (string, int) {
|
||||
t.m2.Lock()
|
||||
defer t.m2.Unlock()
|
||||
return t.sc, t.s
|
||||
type unparsedServiceConfig struct {
|
||||
serviceconfig.Config
|
||||
config string
|
||||
}
|
||||
|
||||
func (t *testClientConn) ParseServiceConfig(string) *serviceconfig.ParseResult {
|
||||
panic("not implemented")
|
||||
func (t *testClientConn) ParseServiceConfig(s string) *serviceconfig.ParseResult {
|
||||
return &serviceconfig.ParseResult{Config: unparsedServiceConfig{config: s}}
|
||||
}
|
||||
|
||||
func (t *testClientConn) ReportError(error) {
|
||||
|
@ -698,33 +691,23 @@ func testDNSResolver(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("%v\n", err)
|
||||
}
|
||||
var addrs []resolver.Address
|
||||
var state resolver.State
|
||||
var cnt int
|
||||
for {
|
||||
addrs, cnt = cc.getAddress()
|
||||
for i := 0; i < 2000; i++ {
|
||||
state, cnt = cc.getState()
|
||||
if cnt > 0 {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
var sc string
|
||||
if a.scWant != "" {
|
||||
for {
|
||||
sc, cnt = cc.getSc()
|
||||
if cnt > 0 {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
} else {
|
||||
// A new service config should never be produced; call getSc once
|
||||
// just in case.
|
||||
sc, _ = cc.getSc()
|
||||
if cnt == 0 {
|
||||
t.Fatalf("UpdateState not called after 2s; aborting")
|
||||
}
|
||||
if !reflect.DeepEqual(a.addrWant, addrs) {
|
||||
t.Errorf("Resolved addresses of target: %q = %+v, want %+v\n", a.target, addrs, a.addrWant)
|
||||
if !reflect.DeepEqual(a.addrWant, state.Addresses) {
|
||||
t.Errorf("Resolved addresses of target: %q = %+v, want %+v\n", a.target, state.Addresses, a.addrWant)
|
||||
}
|
||||
if !reflect.DeepEqual(a.scWant, sc) {
|
||||
sc := scFromState(state)
|
||||
if a.scWant != sc {
|
||||
t.Errorf("Resolved service config of target: %q = %+v, want %+v\n", a.target, sc, a.scWant)
|
||||
}
|
||||
r.Close()
|
||||
|
@ -754,7 +737,7 @@ func testDNSResolverWithSRV(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"srv.ipv4.single.fake",
|
||||
[]resolver.Address{{Addr: "1.2.3.4:1234", Type: resolver.GRPCLB, ServerName: "ipv4.single.fake"}, {Addr: "2.4.6.8" + colonDefaultPort}},
|
||||
[]resolver.Address{{Addr: "2.4.6.8" + colonDefaultPort}, {Addr: "1.2.3.4:1234", Type: resolver.GRPCLB, ServerName: "ipv4.single.fake"}},
|
||||
generateSC("srv.ipv4.single.fake"),
|
||||
},
|
||||
{
|
||||
|
@ -789,36 +772,26 @@ func testDNSResolverWithSRV(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("%v\n", err)
|
||||
}
|
||||
var addrs []resolver.Address
|
||||
defer r.Close()
|
||||
var state resolver.State
|
||||
var cnt int
|
||||
for {
|
||||
addrs, cnt = cc.getAddress()
|
||||
for i := 0; i < 2000; i++ {
|
||||
state, cnt = cc.getState()
|
||||
if cnt > 0 {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
var sc string
|
||||
if a.scWant != "" {
|
||||
for {
|
||||
sc, cnt = cc.getSc()
|
||||
if cnt > 0 {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
} else {
|
||||
// A new service config should never be produced; call getSc once
|
||||
// just in case.
|
||||
sc, _ = cc.getSc()
|
||||
if cnt == 0 {
|
||||
t.Fatalf("UpdateState not called after 2s; aborting")
|
||||
}
|
||||
if !reflect.DeepEqual(a.addrWant, addrs) {
|
||||
t.Errorf("Resolved addresses of target: %q = %+v, want %+v\n", a.target, addrs, a.addrWant)
|
||||
if !reflect.DeepEqual(a.addrWant, state.Addresses) {
|
||||
t.Errorf("Resolved addresses of target: %q = %+v, want %+v\n", a.target, state.Addresses, a.addrWant)
|
||||
}
|
||||
if !reflect.DeepEqual(a.scWant, sc) {
|
||||
sc := scFromState(state)
|
||||
if a.scWant != sc {
|
||||
t.Errorf("Resolved service config of target: %q = %+v, want %+v\n", a.target, sc, a.scWant)
|
||||
}
|
||||
r.Close()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -867,55 +840,47 @@ func testDNSResolveNow(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("%v\n", err)
|
||||
}
|
||||
var addrs []resolver.Address
|
||||
defer r.Close()
|
||||
var state resolver.State
|
||||
var cnt int
|
||||
for {
|
||||
addrs, cnt = cc.getAddress()
|
||||
for i := 0; i < 2000; i++ {
|
||||
state, cnt = cc.getState()
|
||||
if cnt > 0 {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
var sc string
|
||||
for {
|
||||
sc, cnt = cc.getSc()
|
||||
if cnt > 0 {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
if cnt == 0 {
|
||||
t.Fatalf("UpdateState not called after 2s; aborting. state=%v", state)
|
||||
}
|
||||
if !reflect.DeepEqual(a.addrWant, addrs) {
|
||||
t.Errorf("Resolved addresses of target: %q = %+v, want %+v\n", a.target, addrs, a.addrWant)
|
||||
if !reflect.DeepEqual(a.addrWant, state.Addresses) {
|
||||
t.Errorf("Resolved addresses of target: %q = %+v, want %+v\n", a.target, state.Addresses, a.addrWant)
|
||||
}
|
||||
if !reflect.DeepEqual(a.scWant, sc) {
|
||||
sc := scFromState(state)
|
||||
if a.scWant != sc {
|
||||
t.Errorf("Resolved service config of target: %q = %+v, want %+v\n", a.target, sc, a.scWant)
|
||||
}
|
||||
|
||||
revertTbl := mutateTbl(a.target)
|
||||
r.ResolveNow(resolver.ResolveNowOptions{})
|
||||
for i := 0; i < 1000; i++ {
|
||||
addrs, cnt = cc.getAddress()
|
||||
// Break if the address list changes or enough redundant updates happen.
|
||||
if !reflect.DeepEqual(addrs, a.addrWant) || cnt > 10 {
|
||||
for i := 0; i < 2000; i++ {
|
||||
state, cnt = cc.getState()
|
||||
if cnt == 2 {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
for i := 0; i < 1000; i++ {
|
||||
sc, cnt = cc.getSc()
|
||||
// Break if the service config changes or enough redundant updates happen.
|
||||
if !reflect.DeepEqual(sc, a.scWant) || cnt > 10 {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
if cnt != 2 {
|
||||
t.Fatalf("UpdateState not called after 2s; aborting. state=%v", state)
|
||||
}
|
||||
if !reflect.DeepEqual(a.addrNext, addrs) {
|
||||
t.Errorf("Resolved addresses of target: %q = %+v, want %+v\n", a.target, addrs, a.addrNext)
|
||||
sc = scFromState(state)
|
||||
if !reflect.DeepEqual(a.addrNext, state.Addresses) {
|
||||
t.Errorf("Resolved addresses of target: %q = %+v, want %+v\n", a.target, state.Addresses, a.addrNext)
|
||||
}
|
||||
if !reflect.DeepEqual(a.scNext, sc) {
|
||||
if a.scNext != sc {
|
||||
t.Errorf("Resolved service config of target: %q = %+v, want %+v\n", a.target, sc, a.scNext)
|
||||
}
|
||||
revertTbl()
|
||||
r.Close()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -946,29 +911,26 @@ func testIPResolver(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("%v\n", err)
|
||||
}
|
||||
var addrs []resolver.Address
|
||||
var state resolver.State
|
||||
var cnt int
|
||||
for {
|
||||
addrs, cnt = cc.getAddress()
|
||||
state, cnt = cc.getState()
|
||||
if cnt > 0 {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
if !reflect.DeepEqual(v.want, addrs) {
|
||||
t.Errorf("Resolved addresses of target: %q = %+v, want %+v\n", v.target, addrs, v.want)
|
||||
if !reflect.DeepEqual(v.want, state.Addresses) {
|
||||
t.Errorf("Resolved addresses of target: %q = %+v, want %+v\n", v.target, state.Addresses, v.want)
|
||||
}
|
||||
r.ResolveNow(resolver.ResolveNowOptions{})
|
||||
for {
|
||||
addrs, cnt = cc.getAddress()
|
||||
if cnt == 2 {
|
||||
break
|
||||
for i := 0; i < 50; i++ {
|
||||
state, cnt = cc.getState()
|
||||
if cnt > 1 {
|
||||
t.Fatalf("Unexpected second call by resolver to UpdateState. state: %v", state)
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
if !reflect.DeepEqual(v.want, addrs) {
|
||||
t.Errorf("Resolved addresses of target: %q = %+v, want %+v\n", v.target, addrs, v.want)
|
||||
}
|
||||
r.Close()
|
||||
}
|
||||
}
|
||||
|
@ -1006,7 +968,7 @@ func TestResolveFunc(t *testing.T) {
|
|||
r.Close()
|
||||
}
|
||||
if !reflect.DeepEqual(err, v.want) {
|
||||
t.Errorf("Build(%q, cc, resolver.BuildOptions{}) = %v, want %v", v.addr, err, v.want)
|
||||
t.Errorf("Build(%q, cc, _) = %v, want %v", v.addr, err, v.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1037,26 +999,23 @@ func TestDisableServiceConfig(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("%v\n", err)
|
||||
}
|
||||
defer r.Close()
|
||||
var cnt int
|
||||
var sc string
|
||||
// First wait for addresses. We know service configs are reported
|
||||
// first, so once addresses have been reported, we can then check to
|
||||
// see whether any configs have been reported..
|
||||
for i := 0; i < 1000; i++ {
|
||||
_, cnt = cc.getAddress()
|
||||
var state resolver.State
|
||||
for i := 0; i < 2000; i++ {
|
||||
state, cnt = cc.getState()
|
||||
if cnt > 0 {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
sc, cnt = cc.getSc()
|
||||
if a.disableServiceConfig && cnt > 0 {
|
||||
t.Errorf("Resolver reported a service config even though lookups are disabled: sc=%v, cnt=%v", sc, cnt)
|
||||
if cnt == 0 {
|
||||
t.Fatalf("UpdateState not called after 2s; aborting")
|
||||
}
|
||||
if !reflect.DeepEqual(a.scWant, sc) {
|
||||
sc := scFromState(state)
|
||||
if a.scWant != sc {
|
||||
t.Errorf("Resolved service config of target: %q = %+v, want %+v\n", a.target, sc, a.scWant)
|
||||
}
|
||||
r.Close()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1068,49 +1027,49 @@ func TestDNSResolverRetry(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("%v\n", err)
|
||||
}
|
||||
var addrs []resolver.Address
|
||||
for {
|
||||
addrs, _ = cc.getAddress()
|
||||
if len(addrs) == 1 {
|
||||
defer r.Close()
|
||||
var state resolver.State
|
||||
for i := 0; i < 2000; i++ {
|
||||
state, _ = cc.getState()
|
||||
if len(state.Addresses) == 1 {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
if len(state.Addresses) != 1 {
|
||||
t.Fatalf("UpdateState not called with 1 address after 2s; aborting. state=%v", state)
|
||||
}
|
||||
want := []resolver.Address{{Addr: "1.2.3.4" + colonDefaultPort}}
|
||||
if !reflect.DeepEqual(want, addrs) {
|
||||
t.Errorf("Resolved addresses of target: %q = %+v, want %+v\n", target, addrs, want)
|
||||
if !reflect.DeepEqual(want, state.Addresses) {
|
||||
t.Errorf("Resolved addresses of target: %q = %+v, want %+v\n", target, state.Addresses, want)
|
||||
}
|
||||
// mutate the host lookup table so the target has 0 address returned.
|
||||
revertTbl := mutateTbl(target)
|
||||
// trigger a resolve that will get empty address list
|
||||
r.ResolveNow(resolver.ResolveNowOptions{})
|
||||
for {
|
||||
addrs, _ = cc.getAddress()
|
||||
if len(addrs) == 0 {
|
||||
for i := 0; i < 2000; i++ {
|
||||
state, _ = cc.getState()
|
||||
if len(state.Addresses) == 0 {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
if len(state.Addresses) != 0 {
|
||||
t.Fatalf("UpdateState not called with 0 address after 2s; aborting. state=%v", state)
|
||||
}
|
||||
revertTbl()
|
||||
// wait for the retry to happen in two seconds.
|
||||
timer := time.NewTimer(2 * time.Second)
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case <-timer.C:
|
||||
break loop
|
||||
default:
|
||||
addrs, _ = cc.getAddress()
|
||||
if len(addrs) != 0 {
|
||||
break loop
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
r.ResolveNow(resolver.ResolveNowOptions{})
|
||||
for i := 0; i < 2000; i++ {
|
||||
state, _ = cc.getState()
|
||||
if len(state.Addresses) == 1 {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
if !reflect.DeepEqual(want, addrs) {
|
||||
t.Errorf("Resolved addresses of target: %q = %+v, want %+v\n", target, addrs, want)
|
||||
if !reflect.DeepEqual(want, state.Addresses) {
|
||||
t.Errorf("Resolved addresses of target: %q = %+v, want %+v\n", target, state.Addresses, want)
|
||||
}
|
||||
r.Close()
|
||||
}
|
||||
|
||||
func TestCustomAuthority(t *testing.T) {
|
||||
|
@ -1297,16 +1256,16 @@ func TestRateLimitedResolve(t *testing.T) {
|
|||
}
|
||||
|
||||
wantAddrs := []resolver.Address{{Addr: "1.2.3.4" + colonDefaultPort}, {Addr: "5.6.7.8" + colonDefaultPort}}
|
||||
var gotAddrs []resolver.Address
|
||||
var state resolver.State
|
||||
for {
|
||||
var cnt int
|
||||
gotAddrs, cnt = cc.getAddress()
|
||||
state, cnt = cc.getState()
|
||||
if cnt > 0 {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
if !reflect.DeepEqual(gotAddrs, wantAddrs) {
|
||||
t.Errorf("Resolved addresses of target: %q = %+v, want %+v\n", target, gotAddrs, wantAddrs)
|
||||
if !reflect.DeepEqual(state.Addresses, wantAddrs) {
|
||||
t.Errorf("Resolved addresses of target: %q = %+v, want %+v\n", target, state.Addresses, wantAddrs)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
// +build go1.13
|
||||
|
||||
/*
|
||||
*
|
||||
* Copyright 2019 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package dns
|
||||
|
||||
import "net"
|
||||
|
||||
func init() {
|
||||
filterError = func(err error) error {
|
||||
if dnsErr, ok := err.(*net.DNSError); ok && dnsErr.IsNotFound {
|
||||
// The name does not exist; not an error.
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue