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