github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/client/servers/manager_test.go (about) 1 package servers_test 2 3 import ( 4 "fmt" 5 "math/rand" 6 "net" 7 "strings" 8 "testing" 9 10 "github.com/hashicorp/nomad/client/servers" 11 "github.com/hashicorp/nomad/helper/testlog" 12 "github.com/stretchr/testify/require" 13 ) 14 15 type fauxAddr struct { 16 Addr string 17 } 18 19 func (fa *fauxAddr) String() string { return fa.Addr } 20 func (fa *fauxAddr) Network() string { return fa.Addr } 21 22 type fauxConnPool struct { 23 // failPct between 0.0 and 1.0 == pct of time a Ping should fail 24 failPct float64 25 } 26 27 func (cp *fauxConnPool) Ping(net.Addr) error { 28 successProb := rand.Float64() 29 if successProb > cp.failPct { 30 return nil 31 } 32 return fmt.Errorf("bad server") 33 } 34 35 func testManager(t *testing.T) (m *servers.Manager) { 36 logger := testlog.HCLogger(t) 37 shutdownCh := make(chan struct{}) 38 m = servers.New(logger, shutdownCh, &fauxConnPool{}) 39 return m 40 } 41 42 func testManagerFailProb(t *testing.T, failPct float64) (m *servers.Manager) { 43 logger := testlog.HCLogger(t) 44 shutdownCh := make(chan struct{}) 45 m = servers.New(logger, shutdownCh, &fauxConnPool{failPct: failPct}) 46 return m 47 } 48 49 func TestServers_SetServers(t *testing.T) { 50 require := require.New(t) 51 m := testManager(t) 52 var num int 53 num = m.NumServers() 54 if num != 0 { 55 t.Fatalf("Expected zero servers to start") 56 } 57 58 s1 := &servers.Server{Addr: &fauxAddr{"server1"}} 59 s2 := &servers.Server{Addr: &fauxAddr{"server2"}} 60 require.True(m.SetServers([]*servers.Server{s1, s2})) 61 require.False(m.SetServers([]*servers.Server{s1, s2})) 62 require.False(m.SetServers([]*servers.Server{s2, s1})) 63 require.Equal(2, m.NumServers()) 64 require.Len(m.GetServers(), 2) 65 66 require.True(m.SetServers([]*servers.Server{s1})) 67 require.Equal(1, m.NumServers()) 68 require.Len(m.GetServers(), 1) 69 70 // Test that the list of servers does not get shuffled 71 // as a side effect when incoming list is equal 72 require.True(m.SetServers([]*servers.Server{s1, s2})) 73 before := m.GetServers() 74 require.False(m.SetServers([]*servers.Server{s1, s2})) 75 after := m.GetServers() 76 require.Equal(before, after) 77 78 // Send a shuffled list, verify original order doesn't change 79 require.False(m.SetServers([]*servers.Server{s2, s1})) 80 afterShuffledInput := m.GetServers() 81 require.Equal(after, afterShuffledInput) 82 } 83 84 func TestServers_FindServer(t *testing.T) { 85 m := testManager(t) 86 87 if m.FindServer() != nil { 88 t.Fatalf("Expected nil return") 89 } 90 91 var srvs []*servers.Server 92 srvs = append(srvs, &servers.Server{Addr: &fauxAddr{"s1"}}) 93 m.SetServers(srvs) 94 if m.NumServers() != 1 { 95 t.Fatalf("Expected one server") 96 } 97 98 s1 := m.FindServer() 99 if s1 == nil { 100 t.Fatalf("Expected non-nil server") 101 } 102 if s1.String() != "s1" { 103 t.Fatalf("Expected s1 server") 104 } 105 106 s1 = m.FindServer() 107 if s1 == nil || s1.String() != "s1" { 108 t.Fatalf("Expected s1 server (still)") 109 } 110 111 srvs = append(srvs, &servers.Server{Addr: &fauxAddr{"s2"}}) 112 m.SetServers(srvs) 113 if m.NumServers() != 2 { 114 t.Fatalf("Expected two servers") 115 } 116 s1 = m.FindServer() 117 118 for _, srv := range srvs { 119 m.NotifyFailedServer(srv) 120 } 121 122 s2 := m.FindServer() 123 if s1.Equal(s2) { 124 t.Fatalf("Expected different server") 125 } 126 } 127 128 func TestServers_New(t *testing.T) { 129 logger := testlog.HCLogger(t) 130 shutdownCh := make(chan struct{}) 131 m := servers.New(logger, shutdownCh, &fauxConnPool{}) 132 if m == nil { 133 t.Fatalf("Manager nil") 134 } 135 } 136 137 func TestServers_NotifyFailedServer(t *testing.T) { 138 m := testManager(t) 139 140 if m.NumServers() != 0 { 141 t.Fatalf("Expected zero servers to start") 142 } 143 144 s1 := &servers.Server{Addr: &fauxAddr{"s1"}} 145 s2 := &servers.Server{Addr: &fauxAddr{"s2"}} 146 147 // Try notifying for a server that is not managed by Manager 148 m.NotifyFailedServer(s1) 149 if m.NumServers() != 0 { 150 t.Fatalf("Expected zero servers to start") 151 } 152 m.SetServers([]*servers.Server{s1}) 153 154 // Test again w/ a server not in the list 155 m.NotifyFailedServer(s2) 156 if m.NumServers() != 1 { 157 t.Fatalf("Expected one server") 158 } 159 160 m.SetServers([]*servers.Server{s1, s2}) 161 if m.NumServers() != 2 { 162 t.Fatalf("Expected two servers") 163 } 164 165 // Grab a server 166 first := m.FindServer() 167 168 // Find the other server 169 second := s1 170 if first.Equal(s1) { 171 second = s2 172 } 173 174 // Fail the other server 175 m.NotifyFailedServer(second) 176 next := m.FindServer() 177 if !next.Equal(first) { 178 t.Fatalf("Expected first server (still)") 179 } 180 181 // Fail the first 182 m.NotifyFailedServer(first) 183 next = m.FindServer() 184 if !next.Equal(second) { 185 t.Fatalf("Expected second server") 186 } 187 188 // Fail the second 189 m.NotifyFailedServer(second) 190 next = m.FindServer() 191 if !next.Equal(first) { 192 t.Fatalf("Expected first server") 193 } 194 } 195 196 func TestServers_NumServers(t *testing.T) { 197 m := testManager(t) 198 var num int 199 num = m.NumServers() 200 if num != 0 { 201 t.Fatalf("Expected zero servers to start") 202 } 203 204 s := &servers.Server{Addr: &fauxAddr{"server1"}} 205 m.SetServers([]*servers.Server{s}) 206 num = m.NumServers() 207 if num != 1 { 208 t.Fatalf("Expected one server after SetServers") 209 } 210 } 211 212 func TestServers_RebalanceServers(t *testing.T) { 213 const failPct = 0.5 214 m := testManagerFailProb(t, failPct) 215 const maxServers = 100 216 const numShuffleTests = 100 217 const uniquePassRate = 0.5 218 219 // Make a huge list of nodes. 220 var srvs []*servers.Server 221 for i := 0; i < maxServers; i++ { 222 nodeName := fmt.Sprintf("s%02d", i) 223 srvs = append(srvs, &servers.Server{Addr: &fauxAddr{nodeName}}) 224 } 225 m.SetServers(srvs) 226 227 // Keep track of how many unique shuffles we get. 228 uniques := make(map[string]struct{}, maxServers) 229 for i := 0; i < numShuffleTests; i++ { 230 m.RebalanceServers() 231 232 var names []string 233 for j := 0; j < maxServers; j++ { 234 server := m.FindServer() 235 m.NotifyFailedServer(server) 236 names = append(names, server.String()) 237 } 238 key := strings.Join(names, "|") 239 uniques[key] = struct{}{} 240 } 241 242 // We have to allow for the fact that there won't always be a unique 243 // shuffle each pass, so we just look for smell here without the test 244 // being flaky. 245 if len(uniques) < int(maxServers*uniquePassRate) { 246 t.Fatalf("unique shuffle ratio too low: %d/%d", len(uniques), maxServers) 247 } 248 }