// NewNetwork creates nodeCount gossip nodes. func NewNetwork(stopper *stop.Stopper, nodeCount int, createResolvers bool) *Network { log.Infof(context.TODO(), "simulating gossip network with %d nodes", nodeCount) n := &Network{ Nodes: []*Node{}, Stopper: stopper, } n.rpcContext = rpc.NewContext( log.AmbientContext{}, &base.Config{Insecure: true}, hlc.NewClock(hlc.UnixNano, time.Nanosecond), n.Stopper, ) var err error n.tlsConfig, err = n.rpcContext.GetServerTLSConfig() if err != nil { log.Fatal(context.TODO(), err) } for i := 0; i < nodeCount; i++ { node, err := n.CreateNode() if err != nil { log.Fatal(context.TODO(), err) } // Build a resolver for each instance or we'll get data races. if createResolvers { r, err := resolver.NewResolverFromAddress(n.Nodes[0].Addr()) if err != nil { log.Fatalf(context.TODO(), "bad gossip address %s: %s", n.Nodes[0].Addr(), err) } node.Gossip.SetResolvers([]resolver.Resolver{r}) } } return n }
// createTestNode creates an rpc server using the specified address, // gossip instance, KV database and a node using the specified slice // of engines. The server, clock and node are returned. If gossipBS is // not nil, the gossip bootstrap address is set to gossipBS. func createTestNode( addr net.Addr, engines []engine.Engine, gossipBS net.Addr, t *testing.T, ) (*grpc.Server, net.Addr, *hlc.Clock, *Node, *stop.Stopper) { cfg := storage.StoreConfig{} stopper := stop.NewStopper() cfg.Clock = hlc.NewClock(hlc.UnixNano) nodeRPCContext := rpc.NewContext(log.AmbientContext{}, nodeTestBaseContext, cfg.Clock, stopper) cfg.ScanInterval = 10 * time.Hour cfg.ConsistencyCheckInterval = 10 * time.Hour grpcServer := rpc.NewServer(nodeRPCContext) serverCfg := makeTestConfig() cfg.Gossip = gossip.NewTest( 0, nodeRPCContext, grpcServer, serverCfg.GossipBootstrapResolvers, stopper, metric.NewRegistry(), ) ln, err := netutil.ListenAndServeGRPC(stopper, grpcServer, addr) if err != nil { t.Fatal(err) } if gossipBS != nil { // Handle possibility of a :0 port specification. if gossipBS.Network() == addr.Network() && gossipBS.String() == addr.String() { gossipBS = ln.Addr() } r, err := resolver.NewResolverFromAddress(gossipBS) if err != nil { t.Fatalf("bad gossip address %s: %s", gossipBS, err) } cfg.Gossip.SetResolvers([]resolver.Resolver{r}) cfg.Gossip.Start(ln.Addr()) } retryOpts := base.DefaultRetryOptions() retryOpts.Closer = stopper.ShouldQuiesce() distSender := kv.NewDistSender(kv.DistSenderConfig{ Clock: cfg.Clock, RPCContext: nodeRPCContext, RPCRetryOptions: &retryOpts, }, cfg.Gossip) cfg.AmbientCtx.Tracer = tracing.NewTracer() sender := kv.NewTxnCoordSender( cfg.AmbientCtx, distSender, cfg.Clock, false, stopper, kv.MakeTxnMetrics(metric.TestSampleInterval), ) cfg.DB = client.NewDB(sender) cfg.Transport = storage.NewDummyRaftTransport() cfg.MetricsSampleInterval = metric.TestSampleInterval node := NewNode(cfg, status.NewMetricsRecorder(cfg.Clock), metric.NewRegistry(), stopper, kv.MakeTxnMetrics(metric.TestSampleInterval), sql.MakeEventLogger(nil)) roachpb.RegisterInternalServer(grpcServer, node) return grpcServer, ln.Addr(), cfg.Clock, node, stopper }
// TestGossipStorage verifies that a gossip node can join the cluster // using the bootstrap hosts in a gossip.Storage object. func TestGossipStorage(t *testing.T) { defer leaktest.AfterTest(t)() stopper := stop.NewStopper() defer stopper.Stop() network := simulation.NewNetwork(stopper, 3, true) // Set storage for each of the nodes. addresses := make(unresolvedAddrSlice, len(network.Nodes)) stores := make([]testStorage, len(network.Nodes)) for i, n := range network.Nodes { addresses[i] = util.MakeUnresolvedAddr(n.Addr().Network(), n.Addr().String()) if err := n.Gossip.SetStorage(&stores[i]); err != nil { t.Fatal(err) } } // Wait for the gossip network to connect. network.RunUntilFullyConnected() // Wait long enough for storage to get the expected number of addresses. testutils.SucceedsSoon(t, func() error { for i := range stores { p := &stores[i] if expected, actual := len(network.Nodes)-1 /* -1 is ourself */, p.Len(); expected != actual { return errors.Errorf("expected %v, got %v (info: %#v)", expected, actual, p.Info().Addresses) } } return nil }) for i := range stores { p := &stores[i] if !p.isRead() { t.Errorf("%d: expected read from storage", i) } if !p.isWrite() { t.Errorf("%d: expected write from storage", i) } p.Lock() gotAddresses := unresolvedAddrSlice(p.info.Addresses) sort.Sort(gotAddresses) var expectedAddresses unresolvedAddrSlice for j, addr := range addresses { if i != j { // skip node's own address expectedAddresses = append(expectedAddresses, addr) } } sort.Sort(expectedAddresses) // Verify all gossip addresses are written to each persistent store. if !reflect.DeepEqual(gotAddresses, expectedAddresses) { t.Errorf("%d: expected addresses: %s, got: %s", i, expectedAddresses, gotAddresses) } p.Unlock() } // Create an unaffiliated gossip node with only itself as a resolver, // leaving it no way to reach the gossip network. node, err := network.CreateNode() if err != nil { t.Fatal(err) } node.Gossip.SetBootstrapInterval(1 * time.Millisecond) r, err := resolver.NewResolverFromAddress(node.Addr()) if err != nil { t.Fatal(err) } node.Gossip.SetResolvers([]resolver.Resolver{r}) if err := network.StartNode(node); err != nil { t.Fatal(err) } // Wait for a bit to ensure no connection. select { case <-time.After(10 * time.Millisecond): // expected outcome... case <-node.Gossip.Connected: t.Fatal("unexpectedly connected to gossip") } // Give the new node storage with info established from a node // in the established network. var ts2 testStorage if err := stores[0].ReadBootstrapInfo(&ts2.info); err != nil { t.Fatal(err) } if err := node.Gossip.SetStorage(&ts2); err != nil { t.Fatal(err) } network.SimulateNetwork(func(cycle int, network *simulation.Network) bool { if cycle > 1000 { t.Fatal("failed to connect to gossip") } select { case <-node.Gossip.Connected: return false default: return true } }) testutils.SucceedsSoon(t, func() error { if expected, actual := len(network.Nodes)-1 /* -1 is ourself */, ts2.Len(); expected != actual { return errors.Errorf("expected %v, got %v (info: %#v)", expected, actual, ts2.Info().Addresses) } return nil }) }