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  }