github.com/DerekStrickland/consul@v1.4.5/agent/consul/rpc_test.go (about)

     1  package consul
     2  
     3  import (
     4  	"bytes"
     5  	"os"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/hashicorp/consul/agent/consul/state"
    10  	"github.com/hashicorp/consul/agent/structs"
    11  	"github.com/hashicorp/consul/testrpc"
    12  	"github.com/hashicorp/consul/testutil/retry"
    13  	"github.com/hashicorp/go-memdb"
    14  	"github.com/hashicorp/net-rpc-msgpackrpc"
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/stretchr/testify/require"
    17  )
    18  
    19  func TestRPC_NoLeader_Fail(t *testing.T) {
    20  	t.Parallel()
    21  	dir1, s1 := testServerWithConfig(t, func(c *Config) {
    22  		c.RPCHoldTimeout = 1 * time.Millisecond
    23  	})
    24  	defer os.RemoveAll(dir1)
    25  	defer s1.Shutdown()
    26  	codec := rpcClient(t, s1)
    27  	defer codec.Close()
    28  
    29  	arg := structs.RegisterRequest{
    30  		Datacenter: "dc1",
    31  		Node:       "foo",
    32  		Address:    "127.0.0.1",
    33  	}
    34  	var out struct{}
    35  
    36  	// Make sure we eventually fail with a no leader error, which we should
    37  	// see given the short timeout.
    38  	err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out)
    39  	if err == nil || err.Error() != structs.ErrNoLeader.Error() {
    40  		t.Fatalf("bad: %v", err)
    41  	}
    42  
    43  	// Now make sure it goes through.
    44  	testrpc.WaitForTestAgent(t, s1.RPC, "dc1")
    45  	err = msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out)
    46  	if err != nil {
    47  		t.Fatalf("bad: %v", err)
    48  	}
    49  }
    50  
    51  func TestRPC_NoLeader_Fail_on_stale_read(t *testing.T) {
    52  	t.Parallel()
    53  	dir1, s1 := testServerWithConfig(t, func(c *Config) {
    54  		c.RPCHoldTimeout = 1 * time.Millisecond
    55  	})
    56  	defer os.RemoveAll(dir1)
    57  	defer s1.Shutdown()
    58  	codec := rpcClient(t, s1)
    59  	defer codec.Close()
    60  
    61  	arg := structs.RegisterRequest{
    62  		Datacenter: "dc1",
    63  		Node:       "foo",
    64  		Address:    "127.0.0.1",
    65  	}
    66  	var out struct{}
    67  
    68  	// Make sure we eventually fail with a no leader error, which we should
    69  	// see given the short timeout.
    70  	err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out)
    71  	if err == nil || err.Error() != structs.ErrNoLeader.Error() {
    72  		t.Fatalf("bad: %v", err)
    73  	}
    74  
    75  	// Until leader has never been known, stale should fail
    76  	getKeysReq := structs.KeyListRequest{
    77  		Datacenter:   "dc1",
    78  		Prefix:       "",
    79  		Seperator:    "/",
    80  		QueryOptions: structs.QueryOptions{AllowStale: true},
    81  	}
    82  	var keyList structs.IndexedKeyList
    83  	if err := msgpackrpc.CallWithCodec(codec, "KVS.ListKeys", &getKeysReq, &keyList); err.Error() != structs.ErrNoLeader.Error() {
    84  		t.Fatalf("expected %v but got err: %v", structs.ErrNoLeader, err)
    85  	}
    86  
    87  	testrpc.WaitForTestAgent(t, s1.RPC, "dc1")
    88  	if err := msgpackrpc.CallWithCodec(codec, "KVS.ListKeys", &getKeysReq, &keyList); err != nil {
    89  		t.Fatalf("Did not expect any error but got err: %v", err)
    90  	}
    91  }
    92  
    93  func TestRPC_NoLeader_Retry(t *testing.T) {
    94  	t.Parallel()
    95  	dir1, s1 := testServerWithConfig(t, func(c *Config) {
    96  		c.RPCHoldTimeout = 10 * time.Second
    97  	})
    98  	defer os.RemoveAll(dir1)
    99  	defer s1.Shutdown()
   100  	codec := rpcClient(t, s1)
   101  	defer codec.Close()
   102  
   103  	arg := structs.RegisterRequest{
   104  		Datacenter: "dc1",
   105  		Node:       "foo",
   106  		Address:    "127.0.0.1",
   107  	}
   108  	var out struct{}
   109  
   110  	// This isn't sure-fire but tries to check that we don't have a
   111  	// leader going into the RPC, so we exercise the retry logic.
   112  	if ok, _ := s1.getLeader(); ok {
   113  		t.Fatalf("should not have a leader yet")
   114  	}
   115  
   116  	// The timeout is long enough to ride out any reasonable leader
   117  	// election.
   118  	err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out)
   119  	if err != nil {
   120  		t.Fatalf("bad: %v", err)
   121  	}
   122  }
   123  
   124  type MockSink struct {
   125  	*bytes.Buffer
   126  	cancel bool
   127  }
   128  
   129  func (m *MockSink) ID() string {
   130  	return "Mock"
   131  }
   132  
   133  func (m *MockSink) Cancel() error {
   134  	m.cancel = true
   135  	return nil
   136  }
   137  
   138  func (m *MockSink) Close() error {
   139  	return nil
   140  }
   141  
   142  func TestRPC_blockingQuery(t *testing.T) {
   143  	t.Parallel()
   144  	dir, s := testServer(t)
   145  	defer os.RemoveAll(dir)
   146  	defer s.Shutdown()
   147  
   148  	require := require.New(t)
   149  	assert := assert.New(t)
   150  
   151  	// Perform a non-blocking query. Note that it's significant that the meta has
   152  	// a zero index in response - the implied opts.MinQueryIndex is also zero but
   153  	// this should not block still.
   154  	{
   155  		var opts structs.QueryOptions
   156  		var meta structs.QueryMeta
   157  		var calls int
   158  		fn := func(ws memdb.WatchSet, state *state.Store) error {
   159  			calls++
   160  			return nil
   161  		}
   162  		if err := s.blockingQuery(&opts, &meta, fn); err != nil {
   163  			t.Fatalf("err: %v", err)
   164  		}
   165  		if calls != 1 {
   166  			t.Fatalf("bad: %d", calls)
   167  		}
   168  	}
   169  
   170  	// Perform a blocking query that gets woken up and loops around once.
   171  	{
   172  		opts := structs.QueryOptions{
   173  			MinQueryIndex: 3,
   174  		}
   175  		var meta structs.QueryMeta
   176  		var calls int
   177  		fn := func(ws memdb.WatchSet, state *state.Store) error {
   178  			if calls == 0 {
   179  				meta.Index = 3
   180  
   181  				fakeCh := make(chan struct{})
   182  				close(fakeCh)
   183  				ws.Add(fakeCh)
   184  			} else {
   185  				meta.Index = 4
   186  			}
   187  			calls++
   188  			return nil
   189  		}
   190  		if err := s.blockingQuery(&opts, &meta, fn); err != nil {
   191  			t.Fatalf("err: %v", err)
   192  		}
   193  		if calls != 2 {
   194  			t.Fatalf("bad: %d", calls)
   195  		}
   196  	}
   197  
   198  	// Perform a blocking query that returns a zero index from blocking func (e.g.
   199  	// no state yet). This should still return an empty response immediately, but
   200  	// with index of 1 and then block on the next attempt. In one sense zero index
   201  	// is not really a valid response from a state method that is not an error but
   202  	// in practice a lot of state store operations do return it unless they
   203  	// explicitly special checks to turn 0 into 1. Often this is not caught or
   204  	// covered by tests but eventually when hit in the wild causes blocking
   205  	// clients to busy loop and burn CPU. This test ensure that blockingQuery
   206  	// systematically does the right thing to prevent future bugs like that.
   207  	{
   208  		opts := structs.QueryOptions{
   209  			MinQueryIndex: 0,
   210  		}
   211  		var meta structs.QueryMeta
   212  		var calls int
   213  		fn := func(ws memdb.WatchSet, state *state.Store) error {
   214  			if opts.MinQueryIndex > 0 {
   215  				// If client requested blocking, block forever. This is simulating
   216  				// waiting for the watched resource to be initialized/written to giving
   217  				// it a non-zero index. Note the timeout on the query options is relied
   218  				// on to stop the test taking forever.
   219  				fakeCh := make(chan struct{})
   220  				ws.Add(fakeCh)
   221  			}
   222  			meta.Index = 0
   223  			calls++
   224  			return nil
   225  		}
   226  		require.NoError(s.blockingQuery(&opts, &meta, fn))
   227  		assert.Equal(1, calls)
   228  		assert.Equal(uint64(1), meta.Index,
   229  			"expect fake index of 1 to force client to block on next update")
   230  
   231  		// Simulate client making next request
   232  		opts.MinQueryIndex = 1
   233  		opts.MaxQueryTime = 20 * time.Millisecond // Don't wait too long
   234  
   235  		// This time we should block even though the func returns index 0 still
   236  		t0 := time.Now()
   237  		require.NoError(s.blockingQuery(&opts, &meta, fn))
   238  		t1 := time.Now()
   239  		assert.Equal(2, calls)
   240  		assert.Equal(uint64(1), meta.Index,
   241  			"expect fake index of 1 to force client to block on next update")
   242  		assert.True(t1.Sub(t0) > 20*time.Millisecond,
   243  			"should have actually blocked waiting for timeout")
   244  
   245  	}
   246  
   247  	// Perform a query that blocks and gets interrupted when the state store
   248  	// is abandoned.
   249  	{
   250  		opts := structs.QueryOptions{
   251  			MinQueryIndex: 3,
   252  		}
   253  		var meta structs.QueryMeta
   254  		var calls int
   255  		fn := func(ws memdb.WatchSet, state *state.Store) error {
   256  			if calls == 0 {
   257  				meta.Index = 3
   258  
   259  				snap, err := s.fsm.Snapshot()
   260  				if err != nil {
   261  					t.Fatalf("err: %v", err)
   262  				}
   263  				defer snap.Release()
   264  
   265  				buf := bytes.NewBuffer(nil)
   266  				sink := &MockSink{buf, false}
   267  				if err := snap.Persist(sink); err != nil {
   268  					t.Fatalf("err: %v", err)
   269  				}
   270  
   271  				if err := s.fsm.Restore(sink); err != nil {
   272  					t.Fatalf("err: %v", err)
   273  				}
   274  			}
   275  			calls++
   276  			return nil
   277  		}
   278  		if err := s.blockingQuery(&opts, &meta, fn); err != nil {
   279  			t.Fatalf("err: %v", err)
   280  		}
   281  		if calls != 1 {
   282  			t.Fatalf("bad: %d", calls)
   283  		}
   284  	}
   285  }
   286  
   287  func TestRPC_ReadyForConsistentReads(t *testing.T) {
   288  	t.Parallel()
   289  	dir, s := testServerWithConfig(t, func(c *Config) {
   290  		c.RPCHoldTimeout = 2 * time.Millisecond
   291  	})
   292  	defer os.RemoveAll(dir)
   293  	defer s.Shutdown()
   294  
   295  	testrpc.WaitForLeader(t, s.RPC, "dc1")
   296  
   297  	if !s.isReadyForConsistentReads() {
   298  		t.Fatal("Server should be ready for consistent reads")
   299  	}
   300  
   301  	s.resetConsistentReadReady()
   302  	err := s.consistentRead()
   303  	if err.Error() != "Not ready to serve consistent reads" {
   304  		t.Fatal("Server should NOT be ready for consistent reads")
   305  	}
   306  
   307  	go func() {
   308  		time.Sleep(100 * time.Millisecond)
   309  		s.setConsistentReadReady()
   310  	}()
   311  
   312  	retry.Run(t, func(r *retry.R) {
   313  		if err := s.consistentRead(); err != nil {
   314  			r.Fatalf("Expected server to be ready for consistent reads, got error %v", err)
   315  		}
   316  	})
   317  }