github.com/mailgun/holster/v4@v4.20.0/discovery/grpc_srv_resolver_test.go (about) 1 package discovery_test 2 3 import ( 4 "io" 5 "log" 6 "net" 7 "net/url" 8 "sync" 9 "testing" 10 "time" 11 12 "github.com/foxcpp/go-mockdns" 13 "github.com/mailgun/holster/v4/discovery" 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 "google.golang.org/grpc/resolver" 17 ) 18 19 type testClientConn struct { 20 resolver.ClientConn // For unimplemented functions 21 target string 22 m1 sync.Mutex 23 state resolver.State 24 updateStateCalls int 25 errChan chan error 26 updateChan chan struct{} 27 updateStateErr error 28 } 29 30 func (t *testClientConn) UpdateState(s resolver.State) error { 31 t.m1.Lock() 32 defer t.m1.Unlock() 33 t.state = s 34 t.updateStateCalls++ 35 t.updateChan <- struct{}{} 36 return t.updateStateErr 37 } 38 39 func (t *testClientConn) ReportError(err error) { 40 t.errChan <- err 41 } 42 43 func TestSrvResolverBuilderSuccess(t *testing.T) { 44 t.Skip("TODO: fix https://github.com/mailgun/holster/issues/150") 45 46 z := map[string]mockdns.Zone{ 47 "srv.example.com.": { 48 SRV: []net.SRV{ 49 { 50 Target: "one.example.com.", 51 Port: 12345, 52 }, 53 }, 54 A: []string{"192.168.1.1"}, 55 }, 56 "one.example.com.": { 57 A: []string{"192.168.1.1", "10.2.1.100"}, 58 }, 59 } 60 dns, err := mockdns.NewServerWithLogger(z, log.New(io.Discard, "", log.LstdFlags), false) 61 require.NoError(t, err) 62 dns.PatchNet(net.DefaultResolver) 63 defer func() { 64 mockdns.UnpatchNet(net.DefaultResolver) 65 dns.Close() 66 }() 67 68 b := discovery.NewGRPCSRVBuilder() 69 cc := &testClientConn{target: "srv.example.com", updateChan: make(chan struct{}, 1), errChan: make(chan error, 10)} 70 target := resolver.Target{URL: url.URL{Path: "srv.example.com:4567"}} 71 r, err := b.Build(target, cc, resolver.BuildOptions{}) 72 require.NoError(t, err) 73 defer r.Close() 74 75 // This request should be ignored by resolver, since we attempt to 76 // resolve SRV records as soon as Build() is called. 77 r.ResolveNow(resolver.ResolveNowOptions{}) 78 79 // UpdateState should have been called once 80 select { 81 case <-cc.updateChan: 82 case <-time.After(time.Second * 3): 83 require.FailNow(t, "timeout waiting for UpdateState() call") 84 } 85 86 assert.Equal(t, "192.168.1.1:12345", cc.state.Addresses[0].Addr) 87 assert.Equal(t, "10.2.1.100:12345", cc.state.Addresses[1].Addr) 88 } 89 90 func TestSrvResolverBuilderNoARecord(t *testing.T) { 91 z := map[string]mockdns.Zone{ 92 "srv.example.com.": { 93 SRV: []net.SRV{ 94 { 95 Target: "one.example.com.", 96 Port: 12345, 97 }, 98 }, 99 }, 100 } 101 dns, err := mockdns.NewServerWithLogger(z, log.New(io.Discard, "", log.LstdFlags), false) 102 require.NoError(t, err) 103 dns.PatchNet(net.DefaultResolver) 104 defer func() { 105 mockdns.UnpatchNet(net.DefaultResolver) 106 dns.Close() 107 }() 108 109 b := discovery.NewGRPCSRVBuilder() 110 cc := &testClientConn{target: "srv.example.com", updateChan: make(chan struct{}, 1), errChan: make(chan error, 10)} 111 target := resolver.Target{URL: url.URL{Path: "srv.example.com"}} 112 r, err := b.Build(target, cc, resolver.BuildOptions{}) 113 require.NoError(t, err) 114 defer r.Close() 115 116 // This request should be ignored, since we attempt to 117 // resolve SRV records as soon as Build() is called. 118 r.ResolveNow(resolver.ResolveNowOptions{}) 119 120 err = <-cc.errChan 121 assert.Error(t, err) 122 assert.Contains(t, err.Error(), "SRV record for 'srv.example.com' contained no valid domain names") 123 124 // Should retry with back off 125 err = <-cc.errChan 126 assert.Error(t, err) 127 assert.Contains(t, err.Error(), "SRV record for 'srv.example.com' contained no valid domain names") 128 } 129 130 func TestSrvResolverBuilderNoSRVRecord(t *testing.T) { 131 z := map[string]mockdns.Zone{ 132 "srv.example.com.": { 133 A: []string{"10.5.4.3"}, 134 }, 135 } 136 dns, err := mockdns.NewServerWithLogger(z, log.New(io.Discard, "", log.LstdFlags), false) 137 require.NoError(t, err) 138 dns.PatchNet(net.DefaultResolver) 139 defer func() { 140 mockdns.UnpatchNet(net.DefaultResolver) 141 dns.Close() 142 }() 143 144 b := discovery.NewGRPCSRVBuilder() 145 cc := &testClientConn{target: "srv.example.com", updateChan: make(chan struct{}, 1), errChan: make(chan error, 10)} 146 147 // SRV lookup will ignore the port number here, only if SRV lookup fails will this port number matter 148 target := resolver.Target{URL: url.URL{Path: "srv.example.com:12345"}} 149 r, err := b.Build(target, cc, resolver.BuildOptions{}) 150 require.NoError(t, err) 151 defer r.Close() 152 153 // This request should be ignored, since we attempt to 154 // resolve SRV records as soon as Build() is called. 155 r.ResolveNow(resolver.ResolveNowOptions{}) 156 157 err = <-cc.errChan 158 assert.Error(t, err) 159 assert.Contains(t, err.Error(), "SRV record lookup err: lookup srv.example.com on") 160 }