1package agent 2 3import ( 4 "bytes" 5 "log" 6 "os" 7 "testing" 8 "time" 9 10 "github.com/hashicorp/serf/client" 11 "github.com/hashicorp/serf/testutil" 12 "github.com/mitchellh/cli" 13) 14 15func TestCommandRun(t *testing.T) { 16 shutdownCh := make(chan struct{}) 17 defer close(shutdownCh) 18 19 ui := new(cli.MockUi) 20 c := &Command{ 21 ShutdownCh: shutdownCh, 22 Ui: ui, 23 } 24 25 ip1, returnFn1 := testutil.TakeIP() 26 defer returnFn1() 27 28 ip2, returnFn2 := testutil.TakeIP() 29 defer returnFn2() 30 31 rpcAddr := ip2.String() + ":11111" 32 33 args := []string{ 34 "-bind", ip1.String(), 35 "-rpc-addr", rpcAddr, 36 } 37 38 resultCh := make(chan int) 39 go func() { 40 resultCh <- c.Run(args) 41 }() 42 43 testutil.Yield() 44 45 // Verify it runs "forever" 46 select { 47 case <-resultCh: 48 t.Fatalf("ended too soon, err: %v", ui.ErrorWriter.String()) 49 case <-time.After(50 * time.Millisecond): 50 } 51 52 // Send a shutdown request 53 shutdownCh <- struct{}{} 54 55 select { 56 case code := <-resultCh: 57 if code != 0 { 58 t.Fatalf("bad code: %d", code) 59 } 60 case <-time.After(2 * time.Second): 61 t.Fatalf("timeout") 62 } 63} 64 65func TestCommandRun_rpc(t *testing.T) { 66 doneCh := make(chan struct{}) 67 shutdownCh := make(chan struct{}) 68 defer func() { 69 close(shutdownCh) 70 <-doneCh 71 }() 72 73 c := &Command{ 74 ShutdownCh: shutdownCh, 75 Ui: new(cli.MockUi), 76 } 77 78 ip1, returnFn1 := testutil.TakeIP() 79 defer returnFn1() 80 81 ip2, returnFn2 := testutil.TakeIP() 82 defer returnFn2() 83 84 rpcAddr := ip2.String() + ":11111" 85 86 args := []string{ 87 "-bind", ip1.String(), 88 "-rpc-addr", rpcAddr, 89 } 90 91 go func() { 92 code := c.Run(args) 93 if code != 0 { 94 log.Printf("bad: %d", code) 95 } 96 97 close(doneCh) 98 }() 99 100 testutil.Yield() 101 102 client, err := client.NewRPCClient(rpcAddr) 103 if err != nil { 104 t.Fatalf("err: %v", err) 105 } 106 defer client.Close() 107 108 members, err := client.Members() 109 if err != nil { 110 t.Fatalf("err: %v", err) 111 } 112 113 if len(members) != 1 { 114 t.Fatalf("bad: %#v", members) 115 } 116} 117 118func TestCommandRun_join(t *testing.T) { 119 ip1, returnFn1 := testutil.TakeIP() 120 defer returnFn1() 121 122 ip2, returnFn2 := testutil.TakeIP() 123 defer returnFn2() 124 125 a1 := testAgent(t, ip1, nil) 126 if err := a1.Start(); err != nil { 127 t.Fatalf("err: %v", err) 128 } 129 defer a1.Shutdown() 130 131 doneCh := make(chan struct{}) 132 shutdownCh := make(chan struct{}) 133 defer func() { 134 close(shutdownCh) 135 <-doneCh 136 }() 137 138 c := &Command{ 139 ShutdownCh: shutdownCh, 140 Ui: new(cli.MockUi), 141 } 142 143 args := []string{ 144 "-bind", ip2.String(), 145 "-join", a1.conf.MemberlistConfig.BindAddr, 146 "-replay", 147 } 148 149 go func() { 150 code := c.Run(args) 151 if code != 0 { 152 log.Printf("bad: %d", code) 153 } 154 155 close(doneCh) 156 }() 157 158 testutil.Yield() 159 160 if len(a1.Serf().Members()) != 2 { 161 t.Fatalf("bad: %#v", a1.Serf().Members()) 162 } 163} 164 165func TestCommandRun_joinFail(t *testing.T) { 166 ip1, returnFn1 := testutil.TakeIP() 167 defer returnFn1() 168 169 ip2, returnFn2 := testutil.TakeIP() 170 defer returnFn2() 171 172 shutdownCh := make(chan struct{}) 173 defer close(shutdownCh) 174 175 c := &Command{ 176 ShutdownCh: shutdownCh, 177 Ui: new(cli.MockUi), 178 } 179 180 args := []string{ 181 "-bind", ip1.String(), 182 "-join", ip2.String(), 183 } 184 185 code := c.Run(args) 186 if code == 0 { 187 t.Fatal("should fail") 188 } 189} 190 191func TestCommandRun_advertiseAddr(t *testing.T) { 192 doneCh := make(chan struct{}) 193 shutdownCh := make(chan struct{}) 194 defer func() { 195 close(shutdownCh) 196 <-doneCh 197 }() 198 199 c := &Command{ 200 ShutdownCh: shutdownCh, 201 Ui: new(cli.MockUi), 202 } 203 204 ip1, returnFn1 := testutil.TakeIP() 205 defer returnFn1() 206 207 ip2, returnFn2 := testutil.TakeIP() 208 defer returnFn2() 209 210 rpcAddr := ip2.String() + ":11111" 211 args := []string{ 212 "-bind", ip1.String(), 213 "-rpc-addr", rpcAddr, 214 "-advertise", "127.0.0.10:12345", 215 } 216 217 go func() { 218 code := c.Run(args) 219 if code != 0 { 220 log.Printf("bad: %d", code) 221 } 222 223 close(doneCh) 224 }() 225 226 testutil.Yield() 227 228 client, err := client.NewRPCClient(rpcAddr) 229 if err != nil { 230 t.Fatalf("err: %v", err) 231 } 232 defer client.Close() 233 234 members, err := client.Members() 235 if err != nil { 236 t.Fatalf("err: %v", err) 237 } 238 239 if len(members) != 1 { 240 t.Fatalf("bad: %#v", members) 241 } 242 243 // Check the addr and port is as advertised! 244 m := members[0] 245 if bytes.Compare(m.Addr, []byte{127, 0, 0, 10}) != 0 { 246 t.Fatalf("bad: %#v", m) 247 } 248 if m.Port != 12345 { 249 t.Fatalf("bad: %#v", m) 250 } 251} 252 253func TestCommandRun_mDNS(t *testing.T) { 254 // mDNS does not work in travis 255 if os.Getenv("TRAVIS") != "" { 256 t.SkipNow() 257 } 258 259 // Start an agent 260 doneCh := make(chan struct{}) 261 shutdownCh := make(chan struct{}) 262 defer func() { 263 close(shutdownCh) 264 <-doneCh 265 }() 266 267 c := &Command{ 268 ShutdownCh: shutdownCh, 269 Ui: new(cli.MockUi), 270 } 271 272 ip1, returnFn1 := testutil.TakeIP() 273 defer returnFn1() 274 275 ip2, returnFn2 := testutil.TakeIP() 276 defer returnFn2() 277 278 ip3, returnFn3 := testutil.TakeIP() 279 defer returnFn3() 280 281 ip4, returnFn4 := testutil.TakeIP() 282 defer returnFn4() 283 284 rpcAddr1 := ip2.String() + ":11111" 285 rpcAddr2 := ip4.String() + ":11111" 286 287 args := []string{ 288 "-node", "foo", 289 "-bind", ip1.String(), 290 "-discover", "test", 291 "-rpc-addr", rpcAddr1, 292 } 293 294 go func() { 295 code := c.Run(args) 296 if code != 0 { 297 log.Printf("bad: %d", code) 298 } 299 close(doneCh) 300 }() 301 302 // Start a second agent 303 doneCh2 := make(chan struct{}) 304 shutdownCh2 := make(chan struct{}) 305 defer func() { 306 close(shutdownCh2) 307 <-doneCh2 308 }() 309 310 c2 := &Command{ 311 ShutdownCh: shutdownCh2, 312 Ui: new(cli.MockUi), 313 } 314 315 args2 := []string{ 316 "-node", "bar", 317 "-bind", ip3.String(), 318 "-discover", "test", 319 "-rpc-addr", rpcAddr2, 320 } 321 322 go func() { 323 code := c2.Run(args2) 324 if code != 0 { 325 log.Printf("bad: %d", code) 326 } 327 close(doneCh2) 328 }() 329 330 time.Sleep(150 * time.Millisecond) 331 332 client, err := client.NewRPCClient(rpcAddr2) 333 if err != nil { 334 t.Fatalf("err: %v", err) 335 } 336 defer client.Close() 337 338 members, err := client.Members() 339 if err != nil { 340 t.Fatalf("err: %v", err) 341 } 342 343 if len(members) != 2 { 344 t.Fatalf("bad: %#v", members) 345 } 346} 347 348func TestCommandRun_retry_join(t *testing.T) { 349 ip1, returnFn1 := testutil.TakeIP() 350 defer returnFn1() 351 352 ip2, returnFn2 := testutil.TakeIP() 353 defer returnFn2() 354 355 a1 := testAgent(t, ip1, nil) 356 if err := a1.Start(); err != nil { 357 t.Fatalf("err: %v", err) 358 } 359 defer a1.Shutdown() 360 361 doneCh := make(chan struct{}) 362 shutdownCh := make(chan struct{}) 363 defer func() { 364 close(shutdownCh) 365 <-doneCh 366 }() 367 368 c := &Command{ 369 ShutdownCh: shutdownCh, 370 Ui: new(cli.MockUi), 371 } 372 373 args := []string{ 374 "-bind", ip2.String(), 375 "-retry-join", a1.conf.MemberlistConfig.BindAddr, 376 "-replay", 377 } 378 379 go func() { 380 code := c.Run(args) 381 if code != 0 { 382 log.Printf("bad: %d", code) 383 } 384 385 close(doneCh) 386 }() 387 388 testutil.Yield() 389 390 if len(a1.Serf().Members()) != 2 { 391 t.Fatalf("bad: %#v", a1.Serf().Members()) 392 } 393} 394 395func TestCommandRun_retry_joinFail(t *testing.T) { 396 shutdownCh := make(chan struct{}) 397 defer close(shutdownCh) 398 399 c := &Command{ 400 ShutdownCh: shutdownCh, 401 Ui: new(cli.MockUi), 402 } 403 404 ip1, returnFn1 := testutil.TakeIP() 405 defer returnFn1() 406 407 ip2, returnFn2 := testutil.TakeIP() 408 defer returnFn2() 409 410 args := []string{ 411 "-bind", ip1.String(), 412 "-retry-join", ip2.String(), 413 "-retry-interval", "1s", 414 "-retry-max", "1", 415 } 416 417 code := c.Run(args) 418 if code == 0 { 419 t.Fatal("should fail") 420 } 421} 422