github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/gossip/storage_test.go (about)

     1  // Copyright 2016 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package gossip_test
    12  
    13  import (
    14  	"context"
    15  	"reflect"
    16  	"sort"
    17  	"strings"
    18  	"testing"
    19  	"time"
    20  
    21  	"github.com/cockroachdb/cockroach/pkg/config/zonepb"
    22  	"github.com/cockroachdb/cockroach/pkg/gossip"
    23  	"github.com/cockroachdb/cockroach/pkg/gossip/resolver"
    24  	"github.com/cockroachdb/cockroach/pkg/gossip/simulation"
    25  	"github.com/cockroachdb/cockroach/pkg/testutils"
    26  	"github.com/cockroachdb/cockroach/pkg/util"
    27  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    28  	"github.com/cockroachdb/cockroach/pkg/util/protoutil"
    29  	"github.com/cockroachdb/cockroach/pkg/util/stop"
    30  	"github.com/cockroachdb/cockroach/pkg/util/syncutil"
    31  	"github.com/cockroachdb/errors"
    32  )
    33  
    34  type testStorage struct {
    35  	syncutil.Mutex
    36  	read, write bool
    37  	info        gossip.BootstrapInfo
    38  }
    39  
    40  func (ts *testStorage) isRead() bool {
    41  	ts.Lock()
    42  	defer ts.Unlock()
    43  	return ts.read
    44  }
    45  
    46  func (ts *testStorage) isWrite() bool {
    47  	ts.Lock()
    48  	defer ts.Unlock()
    49  	return ts.write
    50  }
    51  
    52  func (ts *testStorage) Info() gossip.BootstrapInfo {
    53  	ts.Lock()
    54  	defer ts.Unlock()
    55  	return ts.info
    56  }
    57  
    58  func (ts *testStorage) Len() int {
    59  	ts.Lock()
    60  	defer ts.Unlock()
    61  	return len(ts.info.Addresses)
    62  }
    63  
    64  func (ts *testStorage) ReadBootstrapInfo(info *gossip.BootstrapInfo) error {
    65  	ts.Lock()
    66  	defer ts.Unlock()
    67  	ts.read = true
    68  	*info = *protoutil.Clone(&ts.info).(*gossip.BootstrapInfo)
    69  	return nil
    70  }
    71  
    72  func (ts *testStorage) WriteBootstrapInfo(info *gossip.BootstrapInfo) error {
    73  	ts.Lock()
    74  	defer ts.Unlock()
    75  	ts.write = true
    76  	ts.info = *protoutil.Clone(info).(*gossip.BootstrapInfo)
    77  	return nil
    78  }
    79  
    80  type unresolvedAddrSlice []util.UnresolvedAddr
    81  
    82  func (s unresolvedAddrSlice) Len() int {
    83  	return len(s)
    84  }
    85  func (s unresolvedAddrSlice) Less(i, j int) bool {
    86  	networkCmp := strings.Compare(s[i].Network(), s[j].Network())
    87  	return networkCmp < 0 || networkCmp == 0 && strings.Compare(s[i].String(), s[j].String()) < 0
    88  }
    89  func (s unresolvedAddrSlice) Swap(i, j int) {
    90  	s[i], s[j] = s[j], s[i]
    91  }
    92  
    93  // TestGossipStorage verifies that a gossip node can join the cluster
    94  // using the bootstrap hosts in a gossip.Storage object.
    95  func TestGossipStorage(t *testing.T) {
    96  	defer leaktest.AfterTest(t)()
    97  	stopper := stop.NewStopper()
    98  	defer stopper.Stop(context.Background())
    99  
   100  	defaultZoneConfig := zonepb.DefaultZoneConfigRef()
   101  	network := simulation.NewNetwork(stopper, 3, true, defaultZoneConfig)
   102  
   103  	// Set storage for each of the nodes.
   104  	addresses := make(unresolvedAddrSlice, len(network.Nodes))
   105  	stores := make([]testStorage, len(network.Nodes))
   106  	for i, n := range network.Nodes {
   107  		addresses[i] = util.MakeUnresolvedAddr(n.Addr().Network(), n.Addr().String())
   108  		if err := n.Gossip.SetStorage(&stores[i]); err != nil {
   109  			t.Fatal(err)
   110  		}
   111  	}
   112  
   113  	// Wait for the gossip network to connect.
   114  	network.RunUntilFullyConnected()
   115  
   116  	// Wait long enough for storage to get the expected number of addresses.
   117  	testutils.SucceedsSoon(t, func() error {
   118  		for i := range stores {
   119  			p := &stores[i]
   120  
   121  			if expected, actual := len(network.Nodes)-1 /* -1 is ourself */, p.Len(); expected != actual {
   122  				return errors.Errorf("expected %v, got %v (info: %#v)", expected, actual, p.Info().Addresses)
   123  			}
   124  		}
   125  		return nil
   126  	})
   127  
   128  	for i := range stores {
   129  		p := &stores[i]
   130  
   131  		if !p.isRead() {
   132  			t.Errorf("%d: expected read from storage", i)
   133  		}
   134  		if !p.isWrite() {
   135  			t.Errorf("%d: expected write from storage", i)
   136  		}
   137  
   138  		p.Lock()
   139  		gotAddresses := unresolvedAddrSlice(p.info.Addresses)
   140  		sort.Sort(gotAddresses)
   141  		var expectedAddresses unresolvedAddrSlice
   142  		for j, addr := range addresses {
   143  			if i != j { // skip node's own address
   144  				expectedAddresses = append(expectedAddresses, addr)
   145  			}
   146  		}
   147  		sort.Sort(expectedAddresses)
   148  
   149  		// Verify all gossip addresses are written to each persistent store.
   150  		if !reflect.DeepEqual(gotAddresses, expectedAddresses) {
   151  			t.Errorf("%d: expected addresses: %s, got: %s", i, expectedAddresses, gotAddresses)
   152  		}
   153  		p.Unlock()
   154  	}
   155  
   156  	// Create an unaffiliated gossip node with only itself as a resolver,
   157  	// leaving it no way to reach the gossip network.
   158  	node, err := network.CreateNode(defaultZoneConfig)
   159  	if err != nil {
   160  		t.Fatal(err)
   161  	}
   162  	node.Gossip.SetBootstrapInterval(1 * time.Millisecond)
   163  
   164  	r, err := resolver.NewResolverFromAddress(node.Addr())
   165  	if err != nil {
   166  		t.Fatal(err)
   167  	}
   168  	node.Resolvers = []resolver.Resolver{r}
   169  	if err := network.StartNode(node); err != nil {
   170  		t.Fatal(err)
   171  	}
   172  
   173  	// Wait for a bit to ensure no connection.
   174  	select {
   175  	case <-time.After(10 * time.Millisecond):
   176  		// expected outcome...
   177  	case <-node.Gossip.Connected:
   178  		t.Fatal("unexpectedly connected to gossip")
   179  	}
   180  
   181  	// Give the new node storage with info established from a node
   182  	// in the established network.
   183  	var ts2 testStorage
   184  	if err := stores[0].ReadBootstrapInfo(&ts2.info); err != nil {
   185  		t.Fatal(err)
   186  	}
   187  	if err := node.Gossip.SetStorage(&ts2); err != nil {
   188  		t.Fatal(err)
   189  	}
   190  
   191  	network.SimulateNetwork(func(cycle int, network *simulation.Network) bool {
   192  		if cycle > 1000 {
   193  			t.Fatal("failed to connect to gossip")
   194  		}
   195  		select {
   196  		case <-node.Gossip.Connected:
   197  			return false
   198  		default:
   199  			return true
   200  		}
   201  	})
   202  
   203  	testutils.SucceedsSoon(t, func() error {
   204  		if expected, actual := len(network.Nodes)-1 /* -1 is ourself */, ts2.Len(); expected != actual {
   205  			return errors.Errorf("expected %v, got %v (info: %#v)", expected, actual, ts2.Info().Addresses)
   206  		}
   207  		return nil
   208  	})
   209  }
   210  
   211  // TestGossipStorageCleanup verifies that bad resolvers are purged
   212  // from the bootstrap info after gossip has successfully connected.
   213  func TestGossipStorageCleanup(t *testing.T) {
   214  	defer leaktest.AfterTest(t)()
   215  	stopper := stop.NewStopper()
   216  	defer stopper.Stop(context.Background())
   217  
   218  	const numNodes = 3
   219  	network := simulation.NewNetwork(stopper, numNodes, false, zonepb.DefaultZoneConfigRef())
   220  
   221  	const notReachableAddr = "localhost:0"
   222  	const invalidAddr = "10.0.0.1000:3333333"
   223  	// Set storage for each of the nodes.
   224  	addresses := make(unresolvedAddrSlice, len(network.Nodes))
   225  	stores := make([]testStorage, len(network.Nodes))
   226  	for i, n := range network.Nodes {
   227  		addresses[i] = util.MakeUnresolvedAddr(n.Addr().Network(), n.Addr().String())
   228  		// Pre-add an invalid address to each gossip storage.
   229  		if err := stores[i].WriteBootstrapInfo(&gossip.BootstrapInfo{
   230  			Addresses: []util.UnresolvedAddr{
   231  				util.MakeUnresolvedAddr("tcp", network.Nodes[(i+1)%numNodes].Addr().String()), // node i+1 address
   232  				util.MakeUnresolvedAddr("tcp", notReachableAddr),                              // unreachable address
   233  				util.MakeUnresolvedAddr("tcp", invalidAddr),                                   // invalid address
   234  			},
   235  		}); err != nil {
   236  			t.Fatal(err)
   237  		}
   238  		if err := n.Gossip.SetStorage(&stores[i]); err != nil {
   239  			t.Fatal(err)
   240  		}
   241  		n.Gossip.SetStallInterval(1 * time.Millisecond)
   242  		n.Gossip.SetBootstrapInterval(1 * time.Millisecond)
   243  	}
   244  
   245  	// Wait for the gossip network to connect.
   246  	network.RunUntilFullyConnected()
   247  
   248  	// Let the gossip network continue running in the background without the
   249  	// simulation cycler preventing it from operating.
   250  	for _, node := range network.Nodes {
   251  		node.Gossip.EnableSimulationCycler(false)
   252  	}
   253  
   254  	// Wait long enough for storage to get the expected number of
   255  	// addresses and no pending cleanups.
   256  	testutils.SucceedsSoon(t, func() error {
   257  		for i := range stores {
   258  			p := &stores[i]
   259  			if expected, actual := len(network.Nodes)-1 /* -1 is ourself */, p.Len(); expected != actual {
   260  				return errors.Errorf("expected %v, got %v (info: %#v)", expected, actual, p.Info().Addresses)
   261  			}
   262  			for _, addr := range p.Info().Addresses {
   263  				if addr.String() == invalidAddr {
   264  					return errors.Errorf("n%d still needs bootstrap cleanup", i)
   265  				}
   266  			}
   267  		}
   268  		return nil
   269  	})
   270  }