github.imxd.top/hashicorp/consul@v1.4.5/agent/router/manager_internal_test.go (about) 1 package router 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 "math/rand" 8 "net" 9 "os" 10 "testing" 11 "time" 12 13 "github.com/hashicorp/consul/agent/metadata" 14 ) 15 16 var ( 17 localLogger *log.Logger 18 localLogBuffer *bytes.Buffer 19 ) 20 21 func init() { 22 localLogBuffer = new(bytes.Buffer) 23 localLogger = log.New(localLogBuffer, "", 0) 24 } 25 26 func GetBufferedLogger() *log.Logger { 27 return localLogger 28 } 29 30 type fauxConnPool struct { 31 // failPct between 0.0 and 1.0 == pct of time a Ping should fail 32 failPct float64 33 } 34 35 func (cp *fauxConnPool) Ping(string, net.Addr, int, bool) (bool, error) { 36 var success bool 37 successProb := rand.Float64() 38 if successProb > cp.failPct { 39 success = true 40 } 41 return success, nil 42 } 43 44 type fauxSerf struct { 45 numNodes int 46 } 47 48 func (s *fauxSerf) NumNodes() int { 49 return s.numNodes 50 } 51 52 func testManager() (m *Manager) { 53 logger := GetBufferedLogger() 54 shutdownCh := make(chan struct{}) 55 m = New(logger, shutdownCh, &fauxSerf{numNodes: 16384}, &fauxConnPool{}) 56 return m 57 } 58 59 func testManagerFailProb(failPct float64) (m *Manager) { 60 logger := GetBufferedLogger() 61 logger = log.New(os.Stderr, "", log.LstdFlags) 62 shutdownCh := make(chan struct{}) 63 m = New(logger, shutdownCh, &fauxSerf{}, &fauxConnPool{failPct: failPct}) 64 return m 65 } 66 67 // func (l *serverList) cycleServer() (servers []*metadata.Server) { 68 func TestManagerInternal_cycleServer(t *testing.T) { 69 m := testManager() 70 l := m.getServerList() 71 72 server0 := &metadata.Server{Name: "server1"} 73 server1 := &metadata.Server{Name: "server2"} 74 server2 := &metadata.Server{Name: "server3"} 75 l.servers = append(l.servers, server0, server1, server2) 76 m.saveServerList(l) 77 78 l = m.getServerList() 79 if len(l.servers) != 3 { 80 t.Fatalf("server length incorrect: %d/3", len(l.servers)) 81 } 82 if l.servers[0] != server0 && 83 l.servers[1] != server1 && 84 l.servers[2] != server2 { 85 t.Fatalf("initial server ordering not correct") 86 } 87 88 l.servers = l.cycleServer() 89 if len(l.servers) != 3 { 90 t.Fatalf("server length incorrect: %d/3", len(l.servers)) 91 } 92 if l.servers[0] != server1 && 93 l.servers[1] != server2 && 94 l.servers[2] != server0 { 95 t.Fatalf("server ordering after one cycle not correct") 96 } 97 98 l.servers = l.cycleServer() 99 if len(l.servers) != 3 { 100 t.Fatalf("server length incorrect: %d/3", len(l.servers)) 101 } 102 if l.servers[0] != server2 && 103 l.servers[1] != server0 && 104 l.servers[2] != server1 { 105 t.Fatalf("server ordering after two cycles not correct") 106 } 107 108 l.servers = l.cycleServer() 109 if len(l.servers) != 3 { 110 t.Fatalf("server length incorrect: %d/3", len(l.servers)) 111 } 112 if l.servers[0] != server0 && 113 l.servers[1] != server1 && 114 l.servers[2] != server2 { 115 t.Fatalf("server ordering after three cycles not correct") 116 } 117 } 118 119 // func (m *Manager) getServerList() serverList { 120 func TestManagerInternal_getServerList(t *testing.T) { 121 m := testManager() 122 l := m.getServerList() 123 if l.servers == nil { 124 t.Fatalf("serverList.servers nil") 125 } 126 127 if len(l.servers) != 0 { 128 t.Fatalf("serverList.servers length not zero") 129 } 130 } 131 132 // func New(logger *log.Logger, shutdownCh chan struct{}, clusterInfo ConsulClusterInfo) (m *Manager) { 133 func TestManagerInternal_New(t *testing.T) { 134 m := testManager() 135 if m == nil { 136 t.Fatalf("Manager nil") 137 } 138 139 if m.clusterInfo == nil { 140 t.Fatalf("Manager.clusterInfo nil") 141 } 142 143 if m.logger == nil { 144 t.Fatalf("Manager.logger nil") 145 } 146 147 if m.shutdownCh == nil { 148 t.Fatalf("Manager.shutdownCh nil") 149 } 150 } 151 152 // func (m *Manager) reconcileServerList(l *serverList) bool { 153 func TestManagerInternal_reconcileServerList(t *testing.T) { 154 tests := []int{0, 1, 2, 3, 4, 5, 10, 100} 155 for _, n := range tests { 156 ok, err := test_reconcileServerList(n) 157 if !ok { 158 t.Errorf("Expected %d to pass: %v", n, err) 159 } 160 } 161 } 162 163 func test_reconcileServerList(maxServers int) (bool, error) { 164 // Build a server list, reconcile, verify the missing servers are 165 // missing, the added have been added, and the original server is 166 // present. 167 const failPct = 0.5 168 m := testManagerFailProb(failPct) 169 170 var failedServers, healthyServers []*metadata.Server 171 for i := 0; i < maxServers; i++ { 172 nodeName := fmt.Sprintf("s%02d", i) 173 174 node := &metadata.Server{Name: nodeName} 175 // Add 66% of servers to Manager 176 if rand.Float64() > 0.33 { 177 m.AddServer(node) 178 179 // Of healthy servers, (ab)use connPoolPinger to 180 // failPct of the servers for the reconcile. This 181 // allows for the selected server to no longer be 182 // healthy for the reconcile below. 183 if ok, _ := m.connPoolPinger.Ping(node.Datacenter, node.Addr, node.Version, node.UseTLS); ok { 184 // Will still be present 185 healthyServers = append(healthyServers, node) 186 } else { 187 // Will be missing 188 failedServers = append(failedServers, node) 189 } 190 } else { 191 // Will be added from the call to reconcile 192 healthyServers = append(healthyServers, node) 193 } 194 } 195 196 // Randomize Manager's server list 197 m.RebalanceServers() 198 selectedServer := m.FindServer() 199 200 var selectedServerFailed bool 201 for _, s := range failedServers { 202 if selectedServer.Key().Equal(s.Key()) { 203 selectedServerFailed = true 204 break 205 } 206 } 207 208 // Update Manager's server list to be "healthy" based on Serf. 209 // Reconcile this with origServers, which is shuffled and has a live 210 // connection, but possibly out of date. 211 origServers := m.getServerList() 212 m.saveServerList(serverList{servers: healthyServers}) 213 214 // This should always succeed with non-zero server lists 215 if !selectedServerFailed && !m.reconcileServerList(&origServers) && 216 len(m.getServerList().servers) != 0 && 217 len(origServers.servers) != 0 { 218 // If the random gods are unfavorable and we end up with zero 219 // length lists, expect things to fail and retry the test. 220 return false, fmt.Errorf("Expected reconcile to succeed: %v %d %d", 221 selectedServerFailed, 222 len(m.getServerList().servers), 223 len(origServers.servers)) 224 } 225 226 // If we have zero-length server lists, test succeeded in degenerate 227 // case. 228 if len(m.getServerList().servers) == 0 && 229 len(origServers.servers) == 0 { 230 // Failed as expected w/ zero length list 231 return true, nil 232 } 233 234 resultingServerMap := make(map[metadata.Key]bool) 235 for _, s := range m.getServerList().servers { 236 resultingServerMap[*s.Key()] = true 237 } 238 239 // Test to make sure no failed servers are in the Manager's 240 // list. Error if there are any failedServers in l.servers 241 for _, s := range failedServers { 242 _, ok := resultingServerMap[*s.Key()] 243 if ok { 244 return false, fmt.Errorf("Found failed server %v in merged list %v", s, resultingServerMap) 245 } 246 } 247 248 // Test to make sure all healthy servers are in the healthy list. 249 if len(healthyServers) != len(m.getServerList().servers) { 250 return false, fmt.Errorf("Expected healthy map and servers to match: %d/%d", len(healthyServers), len(healthyServers)) 251 } 252 253 // Test to make sure all healthy servers are in the resultingServerMap list. 254 for _, s := range healthyServers { 255 _, ok := resultingServerMap[*s.Key()] 256 if !ok { 257 return false, fmt.Errorf("Server %v missing from healthy map after merged lists", s) 258 } 259 } 260 return true, nil 261 } 262 263 // func (l *serverList) refreshServerRebalanceTimer() { 264 func TestManagerInternal_refreshServerRebalanceTimer(t *testing.T) { 265 type clusterSizes struct { 266 numNodes int 267 numServers int 268 minRebalance time.Duration 269 } 270 clusters := []clusterSizes{ 271 {0, 3, 2 * time.Minute}, 272 {1, 0, 2 * time.Minute}, // partitioned cluster 273 {1, 3, 2 * time.Minute}, 274 {2, 3, 2 * time.Minute}, 275 {100, 0, 2 * time.Minute}, // partitioned 276 {100, 1, 2 * time.Minute}, // partitioned 277 {100, 3, 2 * time.Minute}, 278 {1024, 1, 2 * time.Minute}, // partitioned 279 {1024, 3, 2 * time.Minute}, // partitioned 280 {1024, 5, 2 * time.Minute}, 281 {16384, 1, 4 * time.Minute}, // partitioned 282 {16384, 2, 2 * time.Minute}, // partitioned 283 {16384, 3, 2 * time.Minute}, // partitioned 284 {16384, 5, 2 * time.Minute}, 285 {65535, 0, 2 * time.Minute}, // partitioned 286 {65535, 1, 8 * time.Minute}, // partitioned 287 {65535, 2, 3 * time.Minute}, // partitioned 288 {65535, 3, 5 * time.Minute}, // partitioned 289 {65535, 5, 3 * time.Minute}, // partitioned 290 {65535, 7, 2 * time.Minute}, 291 {1000000, 1, 4 * time.Hour}, // partitioned 292 {1000000, 2, 2 * time.Hour}, // partitioned 293 {1000000, 3, 80 * time.Minute}, // partitioned 294 {1000000, 5, 50 * time.Minute}, // partitioned 295 {1000000, 11, 20 * time.Minute}, // partitioned 296 {1000000, 19, 10 * time.Minute}, 297 } 298 299 logger := log.New(os.Stderr, "", log.LstdFlags) 300 shutdownCh := make(chan struct{}) 301 302 for _, s := range clusters { 303 m := New(logger, shutdownCh, &fauxSerf{numNodes: s.numNodes}, &fauxConnPool{}) 304 for i := 0; i < s.numServers; i++ { 305 nodeName := fmt.Sprintf("s%02d", i) 306 m.AddServer(&metadata.Server{Name: nodeName}) 307 } 308 309 d := m.refreshServerRebalanceTimer() 310 if d < s.minRebalance { 311 t.Errorf("duration too short for cluster of size %d and %d servers (%s < %s)", s.numNodes, s.numServers, d, s.minRebalance) 312 } 313 } 314 } 315 316 // func (m *Manager) saveServerList(l serverList) { 317 func TestManagerInternal_saveServerList(t *testing.T) { 318 m := testManager() 319 320 // Initial condition 321 func() { 322 l := m.getServerList() 323 if len(l.servers) != 0 { 324 t.Fatalf("Manager.saveServerList failed to load init config") 325 } 326 327 newServer := new(metadata.Server) 328 l.servers = append(l.servers, newServer) 329 m.saveServerList(l) 330 }() 331 332 // Test that save works 333 func() { 334 l1 := m.getServerList() 335 t1NumServers := len(l1.servers) 336 if t1NumServers != 1 { 337 t.Fatalf("Manager.saveServerList failed to save mutated config") 338 } 339 }() 340 341 // Verify mutation w/o a save doesn't alter the original 342 func() { 343 newServer := new(metadata.Server) 344 l := m.getServerList() 345 l.servers = append(l.servers, newServer) 346 347 l_orig := m.getServerList() 348 origNumServers := len(l_orig.servers) 349 if origNumServers >= len(l.servers) { 350 t.Fatalf("Manager.saveServerList unsaved config overwrote original") 351 } 352 }() 353 }