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  }