github.com/alpe/etcd@v0.1.2-0.20130915230056-09f31af88aeb/etcd_test.go (about)

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