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 }