github.com/kafkaliu/etcd@v0.1.2-0.20131007164923-44c16dd30d69/etcd_test.go (about) 1 /* 2 Copyright 2013 CoreOS Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package main 18 19 import ( 20 "fmt" 21 "math/rand" 22 "net/http" 23 "net/http/httptest" 24 "net/url" 25 "os" 26 "strconv" 27 "strings" 28 "testing" 29 "time" 30 31 "github.com/coreos/etcd/test" 32 "github.com/coreos/go-etcd/etcd" 33 ) 34 35 // Create a single node and try to set value 36 func TestSingleNode(t *testing.T) { 37 procAttr := new(os.ProcAttr) 38 procAttr.Files = []*os.File{nil, os.Stdout, os.Stderr} 39 args := []string{"etcd", "-n=node1", "-f", "-d=/tmp/node1"} 40 41 process, err := os.StartProcess("etcd", args, procAttr) 42 if err != nil { 43 t.Fatal("start process failed:" + err.Error()) 44 return 45 } 46 defer process.Kill() 47 48 time.Sleep(time.Second) 49 50 c := etcd.NewClient() 51 52 c.SyncCluster() 53 // Test Set 54 result, err := c.Set("foo", "bar", 100) 55 56 if err != nil || result.Key != "/foo" || result.Value != "bar" || result.TTL < 95 { 57 if err != nil { 58 t.Fatal(err) 59 } 60 61 t.Fatalf("Set 1 failed with %s %s %v", result.Key, result.Value, result.TTL) 62 } 63 64 time.Sleep(time.Second) 65 66 result, err = c.Set("foo", "bar", 100) 67 68 if err != nil || result.Key != "/foo" || result.Value != "bar" || result.PrevValue != "bar" || result.TTL != 99 { 69 if err != nil { 70 t.Fatal(err) 71 } 72 t.Fatalf("Set 2 failed with %s %s %v", result.Key, result.Value, result.TTL) 73 } 74 75 // Add a test-and-set test 76 77 // First, we'll test we can change the value if we get it write 78 result, match, err := c.TestAndSet("foo", "bar", "foobar", 100) 79 80 if err != nil || result.Key != "/foo" || result.Value != "foobar" || result.PrevValue != "bar" || result.TTL != 99 || !match { 81 if err != nil { 82 t.Fatal(err) 83 } 84 t.Fatalf("Set 3 failed with %s %s %v", result.Key, result.Value, result.TTL) 85 } 86 87 // Next, we'll make sure we can't set it without the correct prior value 88 _, _, err = c.TestAndSet("foo", "bar", "foofoo", 100) 89 90 if err == nil { 91 t.Fatalf("Set 4 expecting error when setting key with incorrect previous value") 92 } 93 94 // Finally, we'll make sure a blank previous value still counts as a test-and-set and still has to match 95 _, _, err = c.TestAndSet("foo", "", "barbar", 100) 96 97 if err == nil { 98 t.Fatalf("Set 5 expecting error when setting key with blank (incorrect) previous value") 99 } 100 } 101 102 // TestInternalVersionFail will ensure that etcd does not come up if the internal raft 103 // versions do not match. 104 func TestInternalVersionFail(t *testing.T) { 105 checkedVersion := false 106 testMux := http.NewServeMux() 107 108 testMux.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) { 109 fmt.Fprintln(w, "This is not a version number") 110 checkedVersion = true 111 }) 112 113 testMux.HandleFunc("/join", func(w http.ResponseWriter, r *http.Request) { 114 t.Fatal("should not attempt to join!") 115 }) 116 117 ts := httptest.NewServer(testMux) 118 defer ts.Close() 119 120 fakeURL, _ := url.Parse(ts.URL) 121 122 procAttr := new(os.ProcAttr) 123 procAttr.Files = []*os.File{nil, os.Stdout, os.Stderr} 124 args := []string{"etcd", "-n=node1", "-f", "-d=/tmp/node1", "-vv", "-C=" + fakeURL.Host} 125 126 process, err := os.StartProcess("etcd", args, procAttr) 127 if err != nil { 128 t.Fatal("start process failed:" + err.Error()) 129 return 130 } 131 defer process.Kill() 132 133 time.Sleep(time.Second) 134 135 _, err = http.Get("http://127.0.0.1:4001") 136 137 if err == nil { 138 t.Fatal("etcd node should not be up") 139 return 140 } 141 142 if checkedVersion == false { 143 t.Fatal("etcd did not check the version") 144 return 145 } 146 } 147 148 // This test creates a single node and then set a value to it. 149 // Then this test kills the node and restart it and tries to get the value again. 150 func TestSingleNodeRecovery(t *testing.T) { 151 procAttr := new(os.ProcAttr) 152 procAttr.Files = []*os.File{nil, os.Stdout, os.Stderr} 153 args := []string{"etcd", "-n=node1", "-d=/tmp/node1"} 154 155 process, err := os.StartProcess("etcd", append(args, "-f"), procAttr) 156 if err != nil { 157 t.Fatal("start process failed:" + err.Error()) 158 return 159 } 160 161 time.Sleep(time.Second) 162 163 c := etcd.NewClient() 164 165 c.SyncCluster() 166 // Test Set 167 result, err := c.Set("foo", "bar", 100) 168 169 if err != nil || result.Key != "/foo" || result.Value != "bar" || result.TTL < 95 { 170 if err != nil { 171 t.Fatal(err) 172 } 173 174 t.Fatalf("Set 1 failed with %s %s %v", result.Key, result.Value, result.TTL) 175 } 176 177 time.Sleep(time.Second) 178 179 process.Kill() 180 181 process, err = os.StartProcess("etcd", args, procAttr) 182 defer process.Kill() 183 if err != nil { 184 t.Fatal("start process failed:" + err.Error()) 185 return 186 } 187 188 time.Sleep(time.Second) 189 190 results, err := c.Get("foo") 191 if err != nil { 192 t.Fatal("get fail: " + err.Error()) 193 return 194 } 195 196 result = results[0] 197 198 if err != nil || result.Key != "/foo" || result.Value != "bar" || result.TTL > 99 { 199 if err != nil { 200 t.Fatal(err) 201 } 202 t.Fatalf("Recovery Get failed with %s %s %v", result.Key, result.Value, result.TTL) 203 } 204 } 205 206 // Create a three nodes and try to set value 207 func templateTestSimpleMultiNode(t *testing.T, tls bool) { 208 procAttr := new(os.ProcAttr) 209 procAttr.Files = []*os.File{nil, os.Stdout, os.Stderr} 210 211 clusterSize := 3 212 213 _, etcds, err := test.CreateCluster(clusterSize, procAttr, tls) 214 215 if err != nil { 216 t.Fatal("cannot create cluster") 217 } 218 219 defer test.DestroyCluster(etcds) 220 221 time.Sleep(time.Second) 222 223 c := etcd.NewClient() 224 225 c.SyncCluster() 226 227 // Test Set 228 result, err := c.Set("foo", "bar", 100) 229 230 if err != nil || result.Key != "/foo" || result.Value != "bar" || result.TTL < 95 { 231 if err != nil { 232 t.Fatal(err) 233 } 234 235 t.Fatalf("Set 1 failed with %s %s %v", result.Key, result.Value, result.TTL) 236 } 237 238 time.Sleep(time.Second) 239 240 result, err = c.Set("foo", "bar", 100) 241 242 if err != nil || result.Key != "/foo" || result.Value != "bar" || result.PrevValue != "bar" || result.TTL != 99 { 243 if err != nil { 244 t.Fatal(err) 245 } 246 t.Fatalf("Set 2 failed with %s %s %v", result.Key, result.Value, result.TTL) 247 } 248 249 } 250 251 func TestSimpleMultiNode(t *testing.T) { 252 templateTestSimpleMultiNode(t, false) 253 } 254 255 func TestSimpleMultiNodeTls(t *testing.T) { 256 templateTestSimpleMultiNode(t, true) 257 } 258 259 // Create a five nodes 260 // Kill all the nodes and restart 261 func TestMultiNodeKillAllAndRecovery(t *testing.T) { 262 procAttr := new(os.ProcAttr) 263 procAttr.Files = []*os.File{nil, os.Stdout, os.Stderr} 264 265 clusterSize := 5 266 argGroup, etcds, err := test.CreateCluster(clusterSize, procAttr, false) 267 268 if err != nil { 269 t.Fatal("cannot create cluster") 270 } 271 272 c := etcd.NewClient() 273 274 c.SyncCluster() 275 276 time.Sleep(time.Second) 277 278 // send 10 commands 279 for i := 0; i < 10; i++ { 280 // Test Set 281 _, err := c.Set("foo", "bar", 0) 282 if err != nil { 283 panic(err) 284 } 285 } 286 287 time.Sleep(time.Second) 288 289 // kill all 290 test.DestroyCluster(etcds) 291 292 time.Sleep(time.Second) 293 294 stop := make(chan bool) 295 leaderChan := make(chan string, 1) 296 all := make(chan bool, 1) 297 298 time.Sleep(time.Second) 299 300 for i := 0; i < clusterSize; i++ { 301 etcds[i], err = os.StartProcess("etcd", argGroup[i], procAttr) 302 } 303 304 go test.Monitor(clusterSize, 1, leaderChan, all, stop) 305 306 <-all 307 <-leaderChan 308 309 result, err := c.Set("foo", "bar", 0) 310 311 if err != nil { 312 panic(err) 313 } 314 315 if result.Index != 18 { 316 t.Fatalf("recovery failed! [%d/18]", result.Index) 317 } 318 319 // kill all 320 test.DestroyCluster(etcds) 321 } 322 323 // Create a five nodes 324 // Randomly kill one of the node and keep on sending set command to the cluster 325 func TestMultiNodeKillOne(t *testing.T) { 326 procAttr := new(os.ProcAttr) 327 procAttr.Files = []*os.File{nil, os.Stdout, os.Stderr} 328 329 clusterSize := 5 330 argGroup, etcds, err := test.CreateCluster(clusterSize, procAttr, false) 331 332 if err != nil { 333 t.Fatal("cannot create cluster") 334 } 335 336 defer test.DestroyCluster(etcds) 337 338 time.Sleep(2 * time.Second) 339 340 c := etcd.NewClient() 341 342 c.SyncCluster() 343 344 stop := make(chan bool) 345 // Test Set 346 go test.Set(stop) 347 348 for i := 0; i < 10; i++ { 349 num := rand.Int() % clusterSize 350 fmt.Println("kill node", num+1) 351 352 // kill 353 etcds[num].Kill() 354 etcds[num].Release() 355 time.Sleep(time.Second) 356 357 // restart 358 etcds[num], err = os.StartProcess("etcd", argGroup[num], procAttr) 359 if err != nil { 360 panic(err) 361 } 362 time.Sleep(time.Second) 363 } 364 fmt.Println("stop") 365 stop <- true 366 <-stop 367 } 368 369 // This test will kill the current leader and wait for the etcd cluster to elect a new leader for 200 times. 370 // It will print out the election time and the average election time. 371 func TestKillLeader(t *testing.T) { 372 procAttr := new(os.ProcAttr) 373 procAttr.Files = []*os.File{nil, os.Stdout, os.Stderr} 374 375 clusterSize := 5 376 argGroup, etcds, err := test.CreateCluster(clusterSize, procAttr, false) 377 378 if err != nil { 379 t.Fatal("cannot create cluster") 380 } 381 382 defer test.DestroyCluster(etcds) 383 384 stop := make(chan bool) 385 leaderChan := make(chan string, 1) 386 all := make(chan bool, 1) 387 388 time.Sleep(time.Second) 389 390 go test.Monitor(clusterSize, 1, leaderChan, all, stop) 391 392 var totalTime time.Duration 393 394 leader := "http://127.0.0.1:7001" 395 396 for i := 0; i < clusterSize; i++ { 397 fmt.Println("leader is ", leader) 398 port, _ := strconv.Atoi(strings.Split(leader, ":")[2]) 399 num := port - 7001 400 fmt.Println("kill server ", num) 401 etcds[num].Kill() 402 etcds[num].Release() 403 404 start := time.Now() 405 for { 406 newLeader := <-leaderChan 407 if newLeader != leader { 408 leader = newLeader 409 break 410 } 411 } 412 take := time.Now().Sub(start) 413 414 totalTime += take 415 avgTime := totalTime / (time.Duration)(i+1) 416 417 fmt.Println("Leader election time is ", take, "with election timeout", ElectionTimeout) 418 fmt.Println("Leader election time average is", avgTime, "with election timeout", ElectionTimeout) 419 etcds[num], err = os.StartProcess("etcd", argGroup[num], procAttr) 420 } 421 stop <- true 422 } 423 424 // TestKillRandom kills random machines in the cluster and 425 // restart them after all other machines agree on the same leader 426 func TestKillRandom(t *testing.T) { 427 procAttr := new(os.ProcAttr) 428 procAttr.Files = []*os.File{nil, os.Stdout, os.Stderr} 429 430 clusterSize := 9 431 argGroup, etcds, err := test.CreateCluster(clusterSize, procAttr, false) 432 433 if err != nil { 434 t.Fatal("cannot create cluster") 435 } 436 437 defer test.DestroyCluster(etcds) 438 439 stop := make(chan bool) 440 leaderChan := make(chan string, 1) 441 all := make(chan bool, 1) 442 443 time.Sleep(3 * time.Second) 444 445 go test.Monitor(clusterSize, 4, leaderChan, all, stop) 446 447 toKill := make(map[int]bool) 448 449 for i := 0; i < 20; i++ { 450 fmt.Printf("TestKillRandom Round[%d/20]\n", i) 451 452 j := 0 453 for { 454 455 r := rand.Int31n(9) 456 if _, ok := toKill[int(r)]; !ok { 457 j++ 458 toKill[int(r)] = true 459 } 460 461 if j > 3 { 462 break 463 } 464 465 } 466 467 for num, _ := range toKill { 468 err := etcds[num].Kill() 469 if err != nil { 470 panic(err) 471 } 472 etcds[num].Wait() 473 } 474 475 time.Sleep(ElectionTimeout) 476 477 <-leaderChan 478 479 for num, _ := range toKill { 480 etcds[num], err = os.StartProcess("etcd", argGroup[num], procAttr) 481 } 482 483 toKill = make(map[int]bool) 484 <-all 485 } 486 487 stop <- true 488 } 489 490 // remove the node and node rejoin with previous log 491 func TestRemoveNode(t *testing.T) { 492 procAttr := new(os.ProcAttr) 493 procAttr.Files = []*os.File{nil, os.Stdout, os.Stderr} 494 495 clusterSize := 3 496 argGroup, etcds, _ := test.CreateCluster(clusterSize, procAttr, false) 497 498 time.Sleep(time.Second) 499 500 c := etcd.NewClient() 501 502 c.SyncCluster() 503 504 rmReq, _ := http.NewRequest("DELETE", "http://127.0.0.1:7001/remove/node3", nil) 505 506 client := &http.Client{} 507 for i := 0; i < 2; i++ { 508 for i := 0; i < 2; i++ { 509 client.Do(rmReq) 510 511 etcds[2].Wait() 512 513 resp, err := c.Get("_etcd/machines") 514 515 if err != nil { 516 panic(err) 517 } 518 519 if len(resp) != 2 { 520 t.Fatal("cannot remove machine") 521 } 522 523 if i == 1 { 524 // rejoin with log 525 etcds[2], err = os.StartProcess("etcd", argGroup[2], procAttr) 526 } else { 527 // rejoin without log 528 etcds[2], err = os.StartProcess("etcd", append(argGroup[2], "-f"), procAttr) 529 } 530 531 if err != nil { 532 panic(err) 533 } 534 535 time.Sleep(time.Second) 536 537 resp, err = c.Get("_etcd/machines") 538 539 if err != nil { 540 panic(err) 541 } 542 543 if len(resp) != 3 { 544 t.Fatal("add machine fails") 545 } 546 } 547 548 // first kill the node, then remove it, then add it back 549 for i := 0; i < 2; i++ { 550 etcds[2].Kill() 551 etcds[2].Wait() 552 553 client.Do(rmReq) 554 555 resp, err := c.Get("_etcd/machines") 556 557 if err != nil { 558 panic(err) 559 } 560 561 if len(resp) != 2 { 562 t.Fatal("cannot remove machine") 563 } 564 565 if i == 1 { 566 // rejoin with log 567 etcds[2], err = os.StartProcess("etcd", append(argGroup[2]), procAttr) 568 } else { 569 // rejoin without log 570 etcds[2], err = os.StartProcess("etcd", append(argGroup[2], "-f"), procAttr) 571 } 572 573 if err != nil { 574 panic(err) 575 } 576 577 time.Sleep(time.Second) 578 579 resp, err = c.Get("_etcd/machines") 580 581 if err != nil { 582 panic(err) 583 } 584 585 if len(resp) != 3 { 586 t.Fatal("add machine fails") 587 } 588 } 589 } 590 test.DestroyCluster(etcds) 591 592 } 593 594 func templateBenchmarkEtcdDirectCall(b *testing.B, tls bool) { 595 procAttr := new(os.ProcAttr) 596 procAttr.Files = []*os.File{nil, os.Stdout, os.Stderr} 597 598 clusterSize := 3 599 _, etcds, _ := test.CreateCluster(clusterSize, procAttr, tls) 600 601 defer test.DestroyCluster(etcds) 602 603 time.Sleep(time.Second) 604 605 b.ResetTimer() 606 for i := 0; i < b.N; i++ { 607 resp, _ := http.Get("http://127.0.0.1:4001/test/speed") 608 resp.Body.Close() 609 } 610 611 } 612 613 func BenchmarkEtcdDirectCall(b *testing.B) { 614 templateBenchmarkEtcdDirectCall(b, false) 615 } 616 617 func BenchmarkEtcdDirectCallTls(b *testing.B) { 618 templateBenchmarkEtcdDirectCall(b, true) 619 }